diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index f811f6ae6a..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -# Disable autocrlf on generated files, they always generate with LF -# Add any extra files or paths here to make git stop saying they -# are changed when only line endings change. -src/generated/**/.cache/cache text eol=lf -src/generated/**/*.json text eol=lf diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f5effbb622..593c7bb0bf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,6 +4,7 @@ on: pull_request: types: - opened + - edited - synchronize - labeled - unlabeled @@ -16,13 +17,13 @@ permissions: jobs: build: - uses: ldtteam/operapublicacreator/.github/workflows/gradle.build.yaml@ng7 + uses: ldtteam/operapublicacreator/.github/workflows/gradle.build.yaml@main with: - java: 21 + java: 17 secrets: inherit pre-release: - uses: ldtteam/operapublicacreator/.github/workflows/gradle.prerelease.yaml@ng7 + uses: ldtteam/operapublicacreator/.github/workflows/gradle.prerelease.yaml@main if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && contains( github.event.pull_request.labels.*.name, 'Pre-release') with: - java: 21 - secrets: inherit \ No newline at end of file + java: 17 + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93f3c93214..030b3ef933 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,8 +14,8 @@ permissions: jobs: release: - uses: ldtteam/operapublicacreator/.github/workflows/gradle.publish.yaml@ng7 + uses: ldtteam/operapublicacreator/.github/workflows/gradle.publish.yaml@main with: - java: 21 + java: 17 curse_release_type: ${{ contains(github.ref, 'release') && 'release' || 'beta' }} - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.gitignore b/.gitignore index 7b0bdf7a7d..a697706818 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ # gradle /build /.gradle +/runs # other /.vscode @@ -24,7 +25,6 @@ /run /runClient /runServer -/runs/ .cache /libs diff --git a/build.gradle b/build.gradle index af45c6a50f..a7614bc901 100644 --- a/build.gradle +++ b/build.gradle @@ -1 +1,74 @@ -apply from: 'https://raw.githubusercontent.com/ldtteam/OperaPublicaCreator/ng7/gradle/mod.gradle' +tableau { + project { + group = "com.ldtteam" + publisher = "LDTTeam" + modId = "structurize" + } + + sourceSets { + api { + isPartOfPrimaryJar = true + neogradle { + isModSource = true + } + } + main { + dependencies { + implementation sourceSets.named('api') + } + } + } + + maven { + publishAsLDTTeamMod() + publishLocally() + pom({ + usingGnu3License() + usingGit() + }) + } +} + +tasks.withType(JavaCompile).configureEach { + options.compilerArgs.addAll(['-Xmaxerrs', '1000', '-Xmaxwarns', '1000']) +} + +// NeoGradle 7.1.x does not strip @OnlyIn annotations that Vineflower adds back +// during decompilation for MC 26.x. The NeoForm and UserDev patches expect them +// removed before nested type declarations. We also restore blank-line separators +// that Vineflower collapses between consecutive declarations. +tasks.matching { it.name == 'neoFormPatch' }.configureEach { + doFirst { + def decompileJar = project.file("build/neoForm/neoFormJoined26.2-2/steps/decompile/outputs.jar") + if (!decompileJar.exists()) return + + def workDir = new File(project.buildDir, "onlyin-fix") + workDir.deleteDir() + workDir.mkdirs() + + def zipTree = project.zipTree(decompileJar) + def javaFiles = [] + zipTree.visit { details -> + def dest = new File(workDir, details.path) + if (details.directory) { dest.mkdirs() } else { dest.parentFile.mkdirs(); details.copyTo(dest); if (dest.name.endsWith('.java')) javaFiles << dest } + } + + javaFiles.each { f -> + def text = f.text + // Strip @OnlyIn when it immediately precedes any nested type declaration + // at any indentation level. Preserves top-level class annotations. + def fixed = text.replaceAll( + /(?m)^( +)@OnlyIn\s*\([^)]*\)\n(\1(?:public |private |protected )?(?:static |final |abstract )?(?:class |interface |record |enum |@interface ))/, + '$2') + // Restore blank lines between closing brace and next declaration at same indent. + fixed = fixed.replaceAll( + /(?m)^(\}+)\n( +(?:public|private|protected)\s+(?:static\s+)?(?:class |record |interface |enum ))/, + '$1\n\n$2') + if (fixed != text) f.text = fixed + } + + def ant = new groovy.ant.AntBuilder() + ant.jar(destfile: decompileJar.absolutePath, basedir: workDir) + workDir.deleteDir() + } +} diff --git a/gradle.properties b/gradle.properties index d45e85b11d..812cfc1068 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,32 +1,26 @@ org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=true -org.gradle.parallel=true -org.gradle.caching=true -org.gradle.configuration-cache=true +org.gradle.daemon=false modId=structurize modGroup=com.ldtteam -modVersion=0.0.1 +local.version=0.0.1-local +modBaseName=structurize -javaVersion=21 +javaVersion=25 useJavaToolChains=true -#The currently running forge. -forgeVersion=21.1.84 +# Minecraft and NeoForge +minecraft.version=26.2 +minecraft.additionalVersions= +neoforge.version=26.2.0.66 -fmlRange=[4,) -forgeRange=[21.0.143,) -minecraftRange=[1.21, 1.22) +fml_range=[11,) +neoforge_range=[26.2,) +minecraft_range=[26.2, 27) +blockUiRange=[0.0.1,) +domumOrnamentumRange=[1.0.0,) -#The version for forge (dependency) -exactMinecraftVersion=1.21.1 -#The main version on curseforge -minecraftVersion=1.21.1 -#Comma seperated list of mc versions, which are marked as compatible on curseforge -additionalMinecraftVersions=1.21 - -blockUiVersion=1.0.191-1.21.1-snapshot -domumOrnamentumVersion=1.0.203-1.21.1-snapshot +usesMCVersionOrderFirst=true githubUrl=https://github.com/ldtteam/Structurize gitUrl=https://github.com/ldtteam/Structurize.git @@ -36,19 +30,14 @@ projectUrl=https://www.curseforge.com/minecraft/mc-mods/Structurize curseId=298744 usesCurse=true -usesParchment=true -parchmentMinecraftVersion=1.21 -parchmentMappingsVersion=2024.07.28 - usesCrowdin=false crowdinId=structurize -usesDatagen=true -additionalModsInDataGen=domum_ornamentum -dataGeneratorsVersion=1.20.4-0.1.57-ALPHA - requiredCurseDependencies=domum-ornamentum;blockui useDefaultTestSystem=true -runtimeSourceSets=main -projectHasApi=false +runtimeSourceSets=api;test;main +librarySourceSets=api;test;main;datagen; +projectHasApi=true +primaryJarClassifier= +usesSonarQube=false diff --git a/gradle/configuration.gradle b/gradle/configuration.gradle index 0ba05f30a2..047a9afdfa 100644 --- a/gradle/configuration.gradle +++ b/gradle/configuration.gradle @@ -1,4 +1,4 @@ project.ext.customChangelogHeader = "\n" + "### Required Dependencies: \n" + - "- BlockUI: ${project.blockUiVersion} (or above)\n" + - "- Domum Ornamentum: ${project.domumOrnamentumVersion} (or above)\n" \ No newline at end of file + "- BlockUI: 0.0.1-local (or above)\n" + + "- Domum Ornamentum: 1.0.0-local (or above)\n" diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 6212501219..e771f1160c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -1,9 +1,14 @@ +repositories { + mavenLocal() +} + dependencies { - implementation "com.ldtteam:blockui:${project.blockUiVersion}" + apiImplementation "com.ldtteam:blockui:0.0.1-local" + implementation "com.ldtteam:blockui:0.0.1-local" - implementation "com.ldtteam:domum-ornamentum:${project.domumOrnamentumVersion}" + apiImplementation "com.ldtteam:domum-ornamentum:1.0.0-local" + implementation "com.ldtteam:domum-ornamentum:1.0.0-local" + runtimeOnly "com.ldtteam:domum-ornamentum:1.0.0-local" - implementation (("com.ldtteam:datagenerators:${project.dataGeneratorsVersion}:universal")) { - transitive = false - } + testImplementation 'junit:junit:4.13.2' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baa..2c3521197d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ff23a68d70..bad7c2462f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a9367..f5feea6d6b 100755 --- a/gradlew +++ b/gradlew @@ -86,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +115,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -205,7 +206,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -213,7 +214,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index db3a6ac207..9d21a21834 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= +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%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%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 diff --git a/settings.gradle b/settings.gradle index 434228644d..ea0715e2cb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,22 @@ +pluginManagement { + repositories { + maven { url = uri('/tmp/maven-local') } + gradlePluginPortal() + maven { + url = uri('https://ldtteam.jfrog.io/artifactory/tableau/') + name = 'Tableau' + } + } +} + plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' + id 'com.ldtteam.tableau' version '0.0.96-j21.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } -gradle.startParameter.setProjectProperties(new HashMap<>(gradle.startParameter.getProjectProperties())) -gradle.startParameter.getProjectProperties().put("build.start", Long.toString(System.currentTimeMillis())) +rootProject.name = 'structurize' -rootProject.name = "structurize" \ No newline at end of file +features { + usesGit = true + usesCurse = true +} diff --git a/src/main/java/com/ldtteam/structurize/api/BlockPosUtil.java b/src/api/java/com/ldtteam/structurize/api/util/BlockPosUtil.java similarity index 90% rename from src/main/java/com/ldtteam/structurize/api/BlockPosUtil.java rename to src/api/java/com/ldtteam/structurize/api/util/BlockPosUtil.java index c42dc0eb47..9a30933e45 100644 --- a/src/main/java/com/ldtteam/structurize/api/BlockPosUtil.java +++ b/src/api/java/com/ldtteam/structurize/api/util/BlockPosUtil.java @@ -1,10 +1,12 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; +import com.ldtteam.structurize.api.util.constant.Constants; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.structure.BoundingBox; import org.jetbrains.annotations.NotNull; @@ -55,10 +57,10 @@ public static void writeToNBT(final CompoundTag compound, final String name, fin */ public static BlockPos readFromNBT(final CompoundTag compound, final String name) { - final CompoundTag coordsCompound = compound.getCompound(name); - final int x = coordsCompound.getInt("x"); - final int y = coordsCompound.getInt("y"); - final int z = coordsCompound.getInt("z"); + final CompoundTag coordsCompound = compound.getCompoundOrEmpty(name); + final int x = coordsCompound.getIntOr("x", 0); + final int y = coordsCompound.getIntOr("y", 0); + final int z = coordsCompound.getIntOr("z", 0); return new BlockPos(x, y, z); } @@ -98,6 +100,27 @@ public static long getDistanceSquared(final BlockPos block1, final BlockPos bloc return result; } + /** + * Get the rotation enum value from the amount of rotations. + * + * @param rotations the amount of rotations. + * @return the enum Rotation. + */ + public static Rotation getRotationFromRotations(final int rotations) + { + switch (rotations) + { + case Constants.ROTATE_ONCE: + return Rotation.CLOCKWISE_90; + case Constants.ROTATE_TWICE: + return Rotation.CLOCKWISE_180; + case Constants.ROTATE_THREE_TIMES: + return Rotation.COUNTERCLOCKWISE_90; + default: + return Rotation.NONE; + } + } + /** * Check if the given position is inside the given corners, corner order does not matter * @@ -250,7 +273,7 @@ public static BlockPos findSafeTeleportPos(@NotNull final Level level, for (final BlockPos start : BlockPos.betweenClosed(target, top)) { - if (target.getY() < level.getMinBuildHeight()) continue; + if (target.getY() < level.getMinY()) continue; for (final BlockPos pos : BlockPos.spiralAround(start, 15, Direction.SOUTH, Direction.EAST)) { diff --git a/src/api/java/com/ldtteam/structurize/api/util/IRotatableBlockEntity.java b/src/api/java/com/ldtteam/structurize/api/util/IRotatableBlockEntity.java new file mode 100644 index 0000000000..6ed81ded11 --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/IRotatableBlockEntity.java @@ -0,0 +1,22 @@ +package com.ldtteam.structurize.api.util; + +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; + +/** + * BlockEntity rotation. + */ +public interface IRotatableBlockEntity +{ + /** + * Rotate the block entity. + * @param rotationIn the rotation. + */ + void rotate(final Rotation rotationIn); + + /** + * Mirror the block entity. + * @param mirror the mirror. + */ + void mirror(final Mirror mirror); +} diff --git a/src/main/java/com/ldtteam/structurize/api/IScrollableItem.java b/src/api/java/com/ldtteam/structurize/api/util/IScrollableItem.java similarity index 73% rename from src/main/java/com/ldtteam/structurize/api/IScrollableItem.java rename to src/api/java/com/ldtteam/structurize/api/util/IScrollableItem.java index 97cf058628..93b1866ad5 100644 --- a/src/main/java/com/ldtteam/structurize/api/IScrollableItem.java +++ b/src/api/java/com/ldtteam/structurize/api/util/IScrollableItem.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; @@ -15,12 +15,11 @@ public interface IScrollableItem * If that succeeds, called again on server side. * @param player the player * @param stack the item stack - * @param deltaX the scroll delta; negative is up, positive is down - * @param deltaY the scroll delta; negative is up, positive is down + * @param delta the scroll delta; negative is up, positive is down * @param ctrlKey the ctrl key is held * @return (client) return SUCCESS to pass to server, FAIL to cancel, or PASS to do normal scrolling. * (server) return value is ignored. */ @NotNull - InteractionResult onMouseScroll(@NotNull Player player, @NotNull ItemStack stack, double deltaX, double deltaY, boolean ctrlKey); + InteractionResult onMouseScroll(@NotNull Player player, @NotNull ItemStack stack, double delta, boolean ctrlKey); } diff --git a/src/main/java/com/ldtteam/structurize/api/ISpecialBlockPickItem.java b/src/api/java/com/ldtteam/structurize/api/util/ISpecialBlockPickItem.java similarity index 93% rename from src/main/java/com/ldtteam/structurize/api/ISpecialBlockPickItem.java rename to src/api/java/com/ldtteam/structurize/api/util/ISpecialBlockPickItem.java index f989a816b3..52d0a09eb8 100644 --- a/src/main/java/com/ldtteam/structurize/api/ISpecialBlockPickItem.java +++ b/src/api/java/com/ldtteam/structurize/api/util/ISpecialBlockPickItem.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionResult; diff --git a/src/api/java/com/ldtteam/structurize/api/util/ItemStackUtils.java b/src/api/java/com/ldtteam/structurize/api/util/ItemStackUtils.java new file mode 100644 index 0000000000..540bd9ba6a --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/ItemStackUtils.java @@ -0,0 +1,364 @@ +package com.ldtteam.structurize.api.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.decoration.ItemFrame; +import net.minecraft.world.entity.vehicle.ContainerEntity; +import net.minecraft.world.entity.vehicle.minecart.MinecartChest; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.transfer.ResourceHandler; +import net.neoforged.neoforge.transfer.item.ItemResource; +import com.mojang.serialization.DynamicOps; +import com.ldtteam.structurize.api.util.Log; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Utility methods for the inventories. + */ +public final class ItemStackUtils +{ + private static final HolderLookup.Provider STATIC_REGISTRIES = + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY); + + /** + * Private constructor to hide the implicit one. + */ + private ItemStackUtils() + { + /* + * Intentionally left empty. + */ + } + + /** + * Get itemStack of tileEntityData. Retrieve the data from the tileEntity. + * + * @param compound the tileEntity stored in a compound. + * @param state the block. + * @return the list of itemstacks. + */ + public static List getItemStacksOfTileEntity(final CompoundTag compound, final BlockState state) + { + if (state.getBlock() instanceof BaseEntityBlock && compound.contains("Items")) + { + // because we're constructing the BlockEntity out-of-world below, chests (and perhaps a few others) + // can't generate an IItemHandler for us, so we need to read the contents manually. + // this could be removed if we always get a "real" BE from a world, but we're called both from a + // real world and from a schematic non-world, and the latter still breaks. + return getItemStacksFromNbt(compound); + } + + BlockPos blockpos = new BlockPos( + compound.getIntOr("x", 0), + compound.getIntOr("y", 0), + compound.getIntOr("z", 0) + ); + final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, state, compound, STATIC_REGISTRIES); + if (tileEntity == null) + { + return Collections.emptyList(); + } + + final List items = new ArrayList<>(); + for (final IItemHandler handler : getItemHandlersFromProvider(tileEntity)) + { + for (int slot = 0; slot < handler.getSlots(); slot++) + { + final ItemStack stack = handler.getStackInSlot(slot); + if (!ItemStackUtils.isEmpty(stack)) + { + items.add(stack); + } + } + } + + return items; + } + + @NotNull + private static List getItemStacksFromNbt(@NotNull final CompoundTag compound) + { + final List items = new ArrayList<>(); + final ListTag listtag = compound.getListOrEmpty("Items"); + + for (int i = 0; i < listtag.size(); ++i) + { + final CompoundTag compoundtag = listtag.getCompoundOrEmpty(i); + final DynamicOps ops = STATIC_REGISTRIES.createSerializationContext(NbtOps.INSTANCE); + final ItemStack stack = ItemStack.CODEC.parse(ops, compoundtag) + .resultOrPartial(error -> { throw new IllegalArgumentException("Invalid item stack NBT: " + error); }) + .orElse(ItemStack.EMPTY); + if (!stack.isEmpty()) + { + items.add(stack); + } + } + + return items; + } + + /** + * Parses an item stack from pre-26 NBT using the built-in registries. + */ + @NotNull + public static ItemStack getItemStackFromNbt(@NotNull final CompoundTag compound) + { + if (compound.isEmpty()) + { + return ItemStack.EMPTY; + } + + return ItemStack.CODEC.parse(STATIC_REGISTRIES.createSerializationContext(NbtOps.INSTANCE), compound) + .resultOrPartial(error -> Log.getLogger().warn("Invalid item stack NBT: {}", error)) + .orElse(ItemStack.EMPTY); + } + + /** + * Writes an item stack using the pre-26 compound representation. + */ + @NotNull + public static CompoundTag writeToNbt(@NotNull final ItemStack stack) + { + if (stack.isEmpty()) + { + return new CompoundTag(); + } + + return ItemStack.CODEC.encodeStart( + STATIC_REGISTRIES.createSerializationContext(NbtOps.INSTANCE), stack) + .resultOrPartial(error -> Log.getLogger().warn("Failed to encode item stack: {}", error)) + .map(tag -> tag instanceof CompoundTag compound ? compound : new CompoundTag()) + .orElseGet(CompoundTag::new); + } + + /** + * Method to get all the IItemHandlers from a given Provider. + * + * @param provider The provider to get the IItemHandlers from. + * @return A list with all the unique IItemHandlers a provider has. + */ + public static Set getItemHandlersFromProvider(final Object provider) + { + final Set handlerSet = new HashSet<>(); + if (provider instanceof final BlockEntity blockEntity && blockEntity.getLevel() != null) + { + for (final Direction side : Direction.values()) + { + addBlockHandler(handlerSet, blockEntity, side); + } + addBlockHandler(handlerSet, blockEntity, null); + } + else if (provider instanceof final Entity entity) + { + final ResourceHandler handler = Capabilities.Item.ENTITY.getCapability(entity, null); + if (handler != null) + { + handlerSet.add(IItemHandler.of(handler)); + } + } + return handlerSet; + } + + private static void addBlockHandler( + final Set handlers, + final BlockEntity blockEntity, + @Nullable final Direction side + ) + { + final ResourceHandler handler = Capabilities.Item.BLOCK.getCapability( + blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, side + ); + if (handler != null) + { + handlers.add(IItemHandler.of(handler)); + } + } + + /** + * Wrapper method to check if a stack is empty. + * Used for easy updating to 1.11. + * + * @param stack The stack to check. + * @return True when the stack is empty, false when not. + */ + public static boolean isEmpty(@Nullable final ItemStack stack) + { + return stack == null || stack.isEmpty() || stack == ItemStack.EMPTY || stack.getCount() <= 0; + } + + /** + * get the size of the stack. + * This is for compatibility between 1.10 and 1.11 + * + * @param stack to get the size from + * @return the size of the stack + */ + public static int getSize(final ItemStack stack) + { + if (ItemStackUtils.isEmpty(stack)) + { + return 0; + } + + return stack.getCount(); + } + + /** + * Get the list of required resources for entities. + * + * @param entity the entity object. + * @param pos the placer pos.. + * @return a list of stacks. + */ + public static List getListOfStackForEntity(final Entity entity, final BlockPos pos) + { + if (entity != null) + { + final List request = new ArrayList<>(); + if (entity instanceof ItemFrame) + { + final ItemStack stack = ((ItemFrame) entity).getItem(); + if (!ItemStackUtils.isEmpty(stack)) + { + stack.setCount(1); + request.add(stack); + } + request.add(new ItemStack(Items.ITEM_FRAME, 1)); + } + else if (entity instanceof ArmorStand) + { + addIfPresent(request, entity.getPickResult()); + if (entity instanceof final LivingEntity livingEntity) + { + for (final EquipmentSlot slot : EquipmentSlot.VALUES) + { + addIfPresent(request, livingEntity.getItemBySlot(slot)); + } + } + } + else if (entity instanceof ContainerEntity containerEntity) + { + addIfPresent(request, entity.getPickResult()); + request.addAll(containerEntity.getItemStacks()); + } + + return request.stream().filter(stack -> !stack.isEmpty()).collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + /** + * Method to compare to stacks, ignoring their stacksize. + * + * @param itemStack1 The left stack to compare. + * @param itemStack2 The right stack to compare. + * @return True when they are equal except the stacksize, false when not. + */ + public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2) + { + return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, true, true); + } + + /** + * Method to compare to stacks, ignoring their stacksize. + * + * @param itemStack1 The left stack to compare. + * @param itemStack2 The right stack to compare. + * @param matchDamage Set to true to match damage data. + * @param matchNBT Set to true to match nbt + * @return True when they are equal except the stacksize, false when not. + */ + public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2, final boolean matchDamage, final boolean matchNBT) + { + return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, matchDamage, matchNBT, false); + } + + /** + * Method to compare to stacks, ignoring their stacksize. + * + * @param itemStack1 The left stack to compare. + * @param itemStack2 The right stack to compare. + * @param matchDamage Set to true to match damage data. + * @param matchNBT Set to true to match nbt + * @param min if the count of stack2 has to be at least the same as stack1. + * @return True when they are equal except the stacksize, false when not. + */ + public static boolean compareItemStacksIgnoreStackSize( + final ItemStack itemStack1, + final ItemStack itemStack2, + final boolean matchDamage, + final boolean matchNBT, + final boolean min) + { + if (isEmpty(itemStack1) && isEmpty(itemStack2)) + { + return true; + } + + if (isEmpty(itemStack1) != isEmpty(itemStack2)) + { + return false; + } + + if (itemStack1.getItem() == itemStack2.getItem() && (!matchDamage || itemStack1.getDamageValue() == itemStack2.getDamageValue())) + { + if (!matchNBT) + { + // Not comparing nbt + return true; + } + + if (min && itemStack1.getCount() > itemStack2.getCount()) + { + return false; + } + + // Data components replace the legacy item NBT map. + if (matchNBT) + { + return ItemStack.matchesIgnoringComponents( + itemStack1.copyWithCount(itemStack2.getCount()), + itemStack2, + type -> !matchDamage && type == DataComponents.DAMAGE + ); + } + else + { + return true; + } + } + return false; + } + + private static void addIfPresent(final List stacks, @Nullable final ItemStack stack) + { + if (stack != null && !stack.isEmpty()) + { + stacks.add(stack); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/api/ItemStorage.java b/src/api/java/com/ldtteam/structurize/api/util/ItemStorage.java similarity index 93% rename from src/main/java/com/ldtteam/structurize/api/ItemStorage.java rename to src/api/java/com/ldtteam/structurize/api/util/ItemStorage.java index d3af56b780..661f147870 100644 --- a/src/main/java/com/ldtteam/structurize/api/ItemStorage.java +++ b/src/api/java/com/ldtteam/structurize/api/util/ItemStorage.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.world.item.Item; @@ -96,7 +96,7 @@ public ItemStorage(final ItemStack stack) */ public ItemStorage(final RegistryFriendlyByteBuf buf) { - this.stack = ItemStackUtils.deserializeFromBuffer(buf); + this.stack = ItemStack.OPTIONAL_STREAM_CODEC.decode(buf); this.shouldIgnoreDamageValue = buf.readBoolean(); this.shouldIgnoreNBTValue = buf.readBoolean(); this.amount = buf.readInt(); @@ -215,24 +215,13 @@ public int getDamageValue() /** * Serialize itemstorage to buffer - * * @param buf */ public void serialize(final RegistryFriendlyByteBuf buf) { - ItemStackUtils.serializeToBuffer(getItemStack(), buf); + ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, getItemStack()); buf.writeBoolean(ignoreDamageValue()); buf.writeBoolean(ignoreNBTValue()); buf.writeInt(getAmount()); } - - /** - * Adder for the quantity. - * - * @param amount the amount to be added. - */ - public void addAmount(final int amount) - { - setAmount(getAmount() + amount); - } -} \ No newline at end of file +} diff --git a/src/main/java/com/ldtteam/structurize/api/Log.java b/src/api/java/com/ldtteam/structurize/api/util/Log.java similarity index 87% rename from src/main/java/com/ldtteam/structurize/api/Log.java rename to src/api/java/com/ldtteam/structurize/api/util/Log.java index 75ce36b187..161933ee0d 100644 --- a/src/main/java/com/ldtteam/structurize/api/Log.java +++ b/src/api/java/com/ldtteam/structurize/api/util/Log.java @@ -1,6 +1,6 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/com/ldtteam/structurize/api/MathUtils.java b/src/api/java/com/ldtteam/structurize/api/util/MathUtils.java similarity index 92% rename from src/main/java/com/ldtteam/structurize/api/MathUtils.java rename to src/api/java/com/ldtteam/structurize/api/util/MathUtils.java index 11e06b3ee2..b48d13e4ed 100644 --- a/src/main/java/com/ldtteam/structurize/api/MathUtils.java +++ b/src/api/java/com/ldtteam/structurize/api/util/MathUtils.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; /** * Useful math stuff to use statically. diff --git a/src/main/java/com/ldtteam/structurize/api/PositionStorage.java b/src/api/java/com/ldtteam/structurize/api/util/PositionStorage.java similarity index 87% rename from src/main/java/com/ldtteam/structurize/api/PositionStorage.java rename to src/api/java/com/ldtteam/structurize/api/util/PositionStorage.java index 1e27d26cff..245284db1b 100644 --- a/src/main/java/com/ldtteam/structurize/api/PositionStorage.java +++ b/src/api/java/com/ldtteam/structurize/api/util/PositionStorage.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.entity.BlockEntity; diff --git a/src/main/java/com/ldtteam/structurize/api/Shape.java b/src/api/java/com/ldtteam/structurize/api/util/Shape.java similarity index 84% rename from src/main/java/com/ldtteam/structurize/api/Shape.java rename to src/api/java/com/ldtteam/structurize/api/util/Shape.java index 9c25d0fcea..2a851cf37b 100644 --- a/src/main/java/com/ldtteam/structurize/api/Shape.java +++ b/src/api/java/com/ldtteam/structurize/api/util/Shape.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; /** * Type of shapes our tool supports. diff --git a/src/api/java/com/ldtteam/structurize/api/util/TriPredicate.java b/src/api/java/com/ldtteam/structurize/api/util/TriPredicate.java new file mode 100644 index 0000000000..e2f03409f6 --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/TriPredicate.java @@ -0,0 +1,10 @@ +package com.ldtteam.structurize.api.util; + +/** + * Predicate for blueprint placement checks, which require three inputs. + */ +@FunctionalInterface +public interface TriPredicate +{ + boolean test(A first, B second, C third); +} diff --git a/src/api/java/com/ldtteam/structurize/api/util/Tuple.java b/src/api/java/com/ldtteam/structurize/api/util/Tuple.java new file mode 100644 index 0000000000..2a8a17d105 --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/Tuple.java @@ -0,0 +1,58 @@ +package com.ldtteam.structurize.api.util; + +import java.util.Objects; + +/** + * Immutable pair of values. Replacement for the removed net.minecraft.util.Tuple. + * + * @param type of the first value. + * @param type of the second value. + */ +public class Tuple +{ + private final A a; + private final B b; + + public Tuple(final A a, final B b) + { + this.a = a; + this.b = b; + } + + public A getA() + { + return a; + } + + public B getB() + { + return b; + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + final Tuple tuple = (Tuple) o; + return Objects.equals(a, tuple.a) && Objects.equals(b, tuple.b); + } + + @Override + public int hashCode() + { + return Objects.hash(a, b); + } + + @Override + public String toString() + { + return "Tuple{" + a + ", " + b + "}"; + } +} diff --git a/src/main/java/com/ldtteam/structurize/api/Utils.java b/src/api/java/com/ldtteam/structurize/api/util/Utils.java similarity index 89% rename from src/main/java/com/ldtteam/structurize/api/Utils.java rename to src/api/java/com/ldtteam/structurize/api/util/Utils.java index b653f05250..fa28a129b6 100644 --- a/src/main/java/com/ldtteam/structurize/api/Utils.java +++ b/src/api/java/com/ldtteam/structurize/api/util/Utils.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; @@ -30,7 +30,7 @@ private Utils() */ public static void playSuccessSound(@NotNull final Player player) { - player.playNotifySound(SoundEvents.NOTE_BLOCK_BELL.value(), SoundSource.NEUTRAL, 1.0f, 1.0f); + player.playSound(SoundEvents.NOTE_BLOCK_BELL.value(), 1.0f, 1.0f); } /** @@ -39,7 +39,7 @@ public static void playSuccessSound(@NotNull final Player player) */ public static void playErrorSound(@NotNull final Player player) { - player.playNotifySound(SoundEvents.NOTE_BLOCK_DIDGERIDOO.value(), SoundSource.NEUTRAL, 1.0f, 0.3f); + player.playSound(SoundEvents.NOTE_BLOCK_DIDGERIDOO.value(), 1.0f, 0.3f); } /** @@ -63,7 +63,7 @@ public static void checkDirectory(final File directory) */ public static boolean nbtContains(final CompoundTag originTag, final CompoundTag compareTag) { - for (final String childTagKey : originTag.getAllKeys()) + for (final String childTagKey : originTag.keySet()) { final Tag originChildTag = originTag.get(childTagKey); final Tag compareChildTag = compareTag.get(childTagKey); diff --git a/src/main/java/com/ldtteam/structurize/api/constants/Constants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/Constants.java similarity index 63% rename from src/main/java/com/ldtteam/structurize/api/constants/Constants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/Constants.java index d2d0b381c3..3ef36d7229 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/Constants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/Constants.java @@ -1,6 +1,5 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import java.util.Random; @@ -12,6 +11,9 @@ public final class Constants { public static final String MOD_ID = "structurize"; public static final String MOD_NAME = "Structurize"; + public static final int ROTATE_ONCE = 1; + public static final int ROTATE_TWICE = 2; + public static final int ROTATE_THREE_TIMES = 3; public static final int TICKS_SECOND = 20; public static final int SECONDS_A_MINUTE = 60; public static final int UPDATE_FLAG = Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS; @@ -28,21 +30,52 @@ public final class Constants public static final String SCANS_FOLDER = "scans"; public static final String SHAPES_FOLDER = "shapes"; + /** + * Maximum message size from client to server (Leaving some extra space). + */ + public static final int MAX_MESSAGE_SIZE = 30_000; + + /** + * Maximum amount of pieces from client to server (Leaving some extra space). + */ + public static final int MAX_AMOUNT_OF_PIECES = 20; + + /** + * Rotation by 90°. + */ + public static final double NINETY_DEGREES = 90D; + /** * Size of the buffer. */ public static final int BUFFER_SIZE = 1024; + + /** + * All possible rotations. + */ + public static final int POSSIBLE_ROTATIONS = 4; + + /** + * Rotation right. + */ + public static final int ROTATE_RIGHT_INDEX = 1; + + /** + * Rotation 180 degree. + */ + public static final int ROTATE_180_INDEX = 2; + + /** + * Rotation left. + */ + public static final int ROTATE_LEFT_INDEX = 3; + /** * Local standard. */ public static final String LOCAL = "Local"; - public static ResourceLocation resLocStruct(final String path) - { - return ResourceLocation.fromNamespaceAndPath(MOD_ID, path); - } - /** * Shared random */ diff --git a/src/main/java/com/ldtteam/structurize/api/constants/GUIConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/GUIConstants.java similarity index 83% rename from src/main/java/com/ldtteam/structurize/api/constants/GUIConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/GUIConstants.java index 1a3bf707b8..b1a1a7468c 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/GUIConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/GUIConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; /** * Constants for UI based values. @@ -8,7 +8,7 @@ public class GUIConstants /** * Default fallback icon. */ - public static final String DEFAULT_ICON = "textures/gui/buildtool/default.png"; + public static final String DEFAULT_ICON = "structurize:textures/gui/buildtool/default.png"; /** * Pre resource string. diff --git a/src/main/java/com/ldtteam/structurize/api/constants/NbtTagConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/NbtTagConstants.java similarity index 52% rename from src/main/java/com/ldtteam/structurize/api/constants/NbtTagConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/NbtTagConstants.java index a89d115143..d4a4a6bbc5 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/NbtTagConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/NbtTagConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; /** * Some constants needed to store things to NBT. @@ -7,6 +7,16 @@ public final class NbtTagConstants { public static final String TAG_UUID = "uuid"; + /** + * Var for first pos string. + */ + public static final String FIRST_POS_STRING = "structurize:start_pos"; + + /** + * Var for second pos string. + */ + public static final String SECOND_POS_STRING = "structurize:end_pos"; + /** * Private constructor to hide the implicit one. */ diff --git a/src/main/java/com/ldtteam/structurize/api/constants/TranslationConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/TranslationConstants.java similarity index 80% rename from src/main/java/com/ldtteam/structurize/api/constants/TranslationConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/TranslationConstants.java index 864805469d..f91dc5bec6 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/TranslationConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/TranslationConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; import org.jetbrains.annotations.NonNls; @@ -12,6 +12,8 @@ public final class TranslationConstants @NonNls public static final String ANCHOR_POS_OUTSIDE_SCHEMATIC = "item.sceptersteel.badanchorpos"; + @NonNls + public static final String NO_VALUE_SPEC = Constants.MOD_ID + ".gui.settings.novaluespec"; @NonNls public static final String GUI_SWITCH_PACK_AUTHORS = "com.ldtteam.structurize.gui.switchpack.authors"; diff --git a/src/main/java/com/ldtteam/structurize/api/constants/WindowConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/WindowConstants.java similarity index 98% rename from src/main/java/com/ldtteam/structurize/api/constants/WindowConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/WindowConstants.java index 60215e6462..7977cc06f9 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/WindowConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/WindowConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; /** * Class which contains all constants required for windows. @@ -317,8 +317,6 @@ public final class WindowConstants */ public static final String REMOVE_FILTERED = "removefiltered"; - public static final String BUTTON_CONTENTS = "contents"; - /** * Display visible blocks checkbox */ diff --git a/src/main/java/com/ldtteam/structurize/api/constants/package-info.java b/src/api/java/com/ldtteam/structurize/api/util/constant/package-info.java similarity index 61% rename from src/main/java/com/ldtteam/structurize/api/constants/package-info.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/package-info.java index 86b97cbe12..e10bf8c213 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/package-info.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/package-info.java @@ -1,4 +1,4 @@ /** * This package contains groups of constants we use at multiple places. */ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; diff --git a/src/main/java/com/ldtteam/structurize/api/package-info.java b/src/api/java/com/ldtteam/structurize/api/util/package-info.java similarity index 69% rename from src/main/java/com/ldtteam/structurize/api/package-info.java rename to src/api/java/com/ldtteam/structurize/api/util/package-info.java index db215d4c0e..c5c6a6da1b 100644 --- a/src/main/java/com/ldtteam/structurize/api/package-info.java +++ b/src/api/java/com/ldtteam/structurize/api/util/package-info.java @@ -1,4 +1,4 @@ /** * This package contains groups of static utility functions we use at multiple places. */ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/blueprint_blacklist.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/blueprint_blacklist.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/blueprint_blacklist.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/blueprint_blacklist.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/good_solid_for_placeholder.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/good_solid_for_placeholder.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/good_solid_for_placeholder.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/good_solid_for_placeholder.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/unsuitable_solid_for_placeholder.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/unsuitable_solid_for_placeholder.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/unsuitable_solid_for_placeholder.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/unsuitable_solid_for_placeholder.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/weak_solid_blocks.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/weak_solid_blocks.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/weak_solid_blocks.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/weak_solid_blocks.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/entity_type/tickable_preview_entities.json b/src/datagen/generated/structurize/data/structurize/tags/entity_types/tickable_preview_entities.json similarity index 70% rename from src/datagen/generated/structurize/data/structurize/tags/entity_type/tickable_preview_entities.json rename to src/datagen/generated/structurize/data/structurize/tags/entity_types/tickable_preview_entities.json index e677f9e8fd..9bab6d5b04 100644 --- a/src/datagen/generated/structurize/data/structurize/tags/entity_type/tickable_preview_entities.json +++ b/src/datagen/generated/structurize/data/structurize/tags/entity_types/tickable_preview_entities.json @@ -5,7 +5,6 @@ "minecraft:block_display", "minecraft:item_display", "minecraft:text_display", - "minecraft:furnace_minecart", - "minecraft:ominous_item_spawner" + "minecraft:furnace_minecart" ] } \ No newline at end of file diff --git a/src/main/java/com/ldtteam/structurize/Network.java b/src/main/java/com/ldtteam/structurize/Network.java new file mode 100644 index 0000000000..2bf20c6555 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/Network.java @@ -0,0 +1,20 @@ +package com.ldtteam.structurize; + +import com.ldtteam.structurize.network.NetworkChannel; + +public class Network +{ + /** + * The network instance. + */ + private static NetworkChannel network = new NetworkChannel("net-channel"); + + /** + * Get the network handler. + * @return the network handler. + */ + public static NetworkChannel getNetwork() + { + return network; + } +} diff --git a/src/main/java/com/ldtteam/structurize/Structurize.java b/src/main/java/com/ldtteam/structurize/Structurize.java index e88cddd406..1e497f9d22 100644 --- a/src/main/java/com/ldtteam/structurize/Structurize.java +++ b/src/main/java/com/ldtteam/structurize/Structurize.java @@ -2,20 +2,19 @@ import com.ldtteam.structurize.blueprints.v1.DataFixerUtils; import com.ldtteam.structurize.blueprints.v1.DataVersion; -import com.ldtteam.structurize.component.ModDataComponents; -import com.ldtteam.structurize.config.ClientConfiguration; -import com.ldtteam.structurize.config.ServerConfiguration; -import com.ldtteam.common.config.Configurations; -import com.ldtteam.common.language.LanguageHandler; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.ModBlocks; +import com.ldtteam.structurize.config.Configuration; import com.ldtteam.structurize.event.ClientEventSubscriber; import com.ldtteam.structurize.event.ClientLifecycleSubscriber; import com.ldtteam.structurize.event.EventSubscriber; import com.ldtteam.structurize.event.LifecycleSubscriber; import com.ldtteam.structurize.items.ModItemGroups; import com.ldtteam.structurize.items.ModItems; +import com.ldtteam.structurize.proxy.ClientProxy; +import com.ldtteam.structurize.proxy.IProxy; +import com.ldtteam.structurize.proxy.ServerProxy; import com.ldtteam.structurize.blockentities.ModBlockEntities; import com.ldtteam.structurize.storage.ClientFutureProcessor; import com.ldtteam.structurize.storage.ServerFutureProcessor; @@ -29,6 +28,7 @@ import net.neoforged.fml.javafmlmod.FMLModContainer; import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.neoforge.common.NeoForge; +import org.jetbrains.annotations.NotNull; /** * Mod main class. @@ -37,44 +37,48 @@ @Mod(Constants.MOD_ID) public class Structurize { + /** + * The proxy. + */ + public static final IProxy proxy = createProxy(); + /** * The config instance. */ - private static Configurations config; + private static Configuration config; /** * Mod init, registers events to their respective busses */ public Structurize(final FMLModContainer modContainer, final Dist dist) { - final IEventBus modBus = modContainer.getEventBus(); - final IEventBus forgeBus = NeoForge.EVENT_BUS; + config = new Configuration(modContainer, modContainer.getEventBus()); - LanguageHandler.loadLangPath("assets/structurize/lang/%s.json"); - config = new Configurations<>(modContainer, modBus, ClientConfiguration::new, ServerConfiguration::new, null); - - ModDataComponents.REGISTRY.register(modBus); - ModBlocks.BLOCKS.register(modBus); - ModItems.ITEMS.register(modBus); - ModBlockEntities.BLOCK_ENTITIES.register(modBus); + final IEventBus modBus = modContainer.getEventBus(); + final IEventBus gameBus = NeoForge.EVENT_BUS; + ModBlocks.getRegistry().register(modBus); + ModItems.getRegistry().register(modBus); + ModBlockEntities.getRegistry().register(modBus); ModItemGroups.TAB_REG.register(modBus); modBus.register(LifecycleSubscriber.class); - forgeBus.register(EventSubscriber.class); + gameBus.register(EventSubscriber.class); - if (FMLEnvironment.dist.isClient()) + if (dist.isClient()) { + gameBus.register(ClientStructurePackLoader.class); + gameBus.register(ClientFutureProcessor.class); modBus.register(ClientLifecycleSubscriber.class); - forgeBus.register(ClientEventSubscriber.class); - - forgeBus.register(ClientStructurePackLoader.class); - forgeBus.register(ClientFutureProcessor.class); + gameBus.register(ClientEventSubscriber.class); + } + else + { + ServerStructurePackLoader.onServerStarting(); } - forgeBus.register(ServerStructurePackLoader.class); - forgeBus.register(ServerFutureProcessor.class); - - forgeBus.register(ServerPreviewDistributor.class); + gameBus.register(ServerStructurePackLoader.class); + gameBus.register(ServerPreviewDistributor.class); + gameBus.register(ServerFutureProcessor.class); if (DataFixerUtils.isVanillaDF) { @@ -82,9 +86,9 @@ public Structurize(final FMLModContainer modContainer, final Dist dist) { throw new RuntimeException("You are trying to run old mod on much newer vanilla. Missing some newest data versions. Please update com/ldtteam/structures/blueprints/v1/DataVersion"); } - else if (!FMLEnvironment.production && DataVersion.CURRENT == DataVersion.UPCOMING) + else if (!FMLEnvironment.isProduction() && DataVersion.CURRENT == DataVersion.UPCOMING) { - throw new RuntimeException("Missing some newest data versions. Please update src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java"); + throw new RuntimeException("Missing some newest data versions. Please update com/ldtteam/structures/blueprints/v1/DataVersion"); } } else @@ -96,12 +100,17 @@ else if (!FMLEnvironment.production && DataVersion.CURRENT == DataVersion.UPCOMI } } + private static IProxy createProxy() + { + return FMLEnvironment.getDist().isClient() ? new ClientProxy() : new ServerProxy(); + } + /** * Get the config handler. * * @return the config handler. */ - public static Configurations getConfig() + public static Configuration getConfig() { return config; } diff --git a/src/main/java/com/ldtteam/structurize/api/IRotatableBlockEntity.java b/src/main/java/com/ldtteam/structurize/api/IRotatableBlockEntity.java deleted file mode 100644 index 28b55d5b7e..0000000000 --- a/src/main/java/com/ldtteam/structurize/api/IRotatableBlockEntity.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ldtteam.structurize.api; - -/** - * BlockEntity rotation. - */ -public interface IRotatableBlockEntity -{ - /** - * Rotates and mirrors block entity. - */ - void rotateAndMirror(RotationMirror rotationMirror); -} diff --git a/src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java b/src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java deleted file mode 100644 index e73e490dbe..0000000000 --- a/src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java +++ /dev/null @@ -1,417 +0,0 @@ -package com.ldtteam.structurize.api; - -import com.ldtteam.common.fakelevel.SingleBlockFakeLevel.SidedSingleBlockFakeLevel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.component.DataComponentMap; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.core.component.DataComponents; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.Container; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.decoration.ItemFrame; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.SpawnEggItem; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.EntityHitResult; -import net.neoforged.neoforge.capabilities.Capabilities.ItemHandler; -import net.neoforged.neoforge.items.IItemHandler; -import net.neoforged.neoforge.items.wrapper.InvWrapper; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; - -/** - * Utility methods for the inventories. - */ -public final class ItemStackUtils -{ - public static final SidedSingleBlockFakeLevel ITEM_HANDLER_FAKE_LEVEL = new SidedSingleBlockFakeLevel(); - - /** - * Private constructor to hide the implicit one. - */ - private ItemStackUtils() - { - /* - * Intentionally left empty. - */ - } - - /** - * Get itemStack of tileEntityData. Retrieve the data from the tileEntity. Including recursive content, eg. shulkers - * - * @param compound the tileEntity stored in a compound. - * @param state the block. - * @param level real vanilla instance for fakeLevel - * @return the list of itemstacks. - * @see #getListOfStackForEntity(Entity, BlockPos) - */ - public static List getItemStacksOfTileEntity(final CompoundTag compound, final BlockState state, final Level level) - { - if (compound == null) - { - return List.of(); - } - - final BlockPos blockpos = new BlockPos(compound.getInt("x"), compound.getInt("y"), compound.getInt("z")); - final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, state, compound, level.registryAccess()); - if (tileEntity == null) - { - return List.of(); - } - - return ITEM_HANDLER_FAKE_LEVEL.get(level).useFakeLevelContext(state, tileEntity, level, fakeLevel -> { - final List items = new ArrayList<>(); - getItemHandlersFromProvider(tileEntity, blockpos, state).forEach(itemHandler -> deepExtractItemHandler(itemHandler, items::add)); - return items; - }); - } - - /** - * @param handler root itemHandler to extract - * @param sink where to put content of all found itemStacks, incl. recursive contents - */ - public static void deepExtractItemHandler(@Nullable final IItemHandler handler, final Consumer sink) - { - if (handler == null) - { - return; - } - - for (int slot = 0; slot < handler.getSlots(); slot++) - { - final ItemStack stack = handler.getStackInSlot(slot).copy(); - if (!ItemStackUtils.isEmpty(stack)) - { - sink.accept(stack); - deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), sink); - } - } - } - - /** - * Method to get sensible item handlers from blockEntity. Tries to provide whole deduplicated content. However this assumption is - * weak. There still might be content (in returned set) that is not present at all or duplicated. - * - * @param provider The provider to get the IItemHandlers from. - * @return A list with all the unique IItemHandlers a provider has. - */ - public static Set getItemHandlersFromProvider(@Nullable final BlockEntity provider, final BlockPos pos, final BlockState state) - { - if (provider == null) - { - return Set.of(); - } - if (provider instanceof final IItemHandler itemHandler) - { - // be is itemHandler itself = easy - return Set.of(itemHandler); - } - if (provider instanceof final Container container) - { - // be is vanilla container = itemHandler cap might return SidedInvWrapper with partial inv view - return Set.of(new InvWrapper(container)); - } - - final IItemHandler unsidedItemHandler = provider.getLevel().getCapability(ItemHandler.BLOCK, pos, state, provider, null); - if (unsidedItemHandler != null) - { - // weak assumption of unsided being partial view only - return Set.of(unsidedItemHandler); - } - - final Set handlerSet = new HashSet<>(); - for (final Direction side : Direction.values()) - { - final IItemHandler cap = provider.getLevel().getCapability(ItemHandler.BLOCK, pos, state, provider, side); - if (cap != null) - { - handlerSet.add(cap); - } - } - // weakest assumption of sided itemHandler having disjoint sides - return handlerSet; - } - - /** - * Wrapper method to check if a stack is empty. - * Used for easy updating to 1.11. - * - * @param stack The stack to check. - * @return True when the stack is empty, false when not. - */ - public static boolean isEmpty(@Nullable final ItemStack stack) - { - return stack == null || stack.isEmpty() || stack == ItemStack.EMPTY || stack.getCount() <= 0; - } - - /** - * get the size of the stack. - * This is for compatibility between 1.10 and 1.11 - * - * @param stack to get the size from - * @return the size of the stack - */ - public static int getSize(final ItemStack stack) - { - if (ItemStackUtils.isEmpty(stack)) - { - return 0; - } - - return stack.getCount(); - } - - /** - * @deprecated {@link #getListOfStackForEntity(Entity)} - */ - @Deprecated(forRemoval = true, since = "1.21.1") - public static List getListOfStackForEntity(final Entity entity, final BlockPos pos) - { - return getListOfStackForEntity(entity); - } - - /** - * Get the list of required resources for entities + entity spawning item. Same implementation as blockEntity logic. Including recursive content, eg. shulkers - * - * @param entity the entity object. - * @return a list of stacks. - * @see #getItemStacksOfTileEntity(BlockEntity) - */ - public static List getListOfStackForEntity(final Entity entity) - { - if (entity == null) - { - return List.of(); - } - - final List request = new ArrayList<>(); - - // process entity itself - final ItemStack spawnItem = getEntitySpawningItem(entity); - if (spawnItem != null && !(spawnItem.getItem() instanceof SpawnEggItem)) - { - request.add(spawnItem); - } - - // process entity contents - request.addAll(getItemStacksOfEntity(entity)); - - return request.stream().filter(stack -> !stack.isEmpty()).toList(); - } - - /** - * Get the list of required resources for entities. Same implementation as blockEntity logic. Including recursive content, eg. shulkers - * - * @param entity the entity object. - * @return a list of stacks. - * @see #getItemStacksOfTileEntity(BlockEntity) - */ - public static List getItemStacksOfEntity(final Entity entity) - { - if (entity == null) - { - return List.of(); - } - - final List entityContent = new ArrayList<>(); - - IItemHandler itemHandler = null; - if (entity instanceof final IItemHandler iitemHandler) - { - // entity is itemHandler itself = easy - itemHandler = iitemHandler; - } - else if (entity instanceof final Container container) - { - // entity is vanilla container = itemHandler cap might return SidedInvWrapper with partial inv view - itemHandler = new InvWrapper(container); - } - if (itemHandler == null) - { - itemHandler = entity.getCapability(ItemHandler.ENTITY); - } - if (itemHandler == null) - { - // weak assumption of unsided being partial view only - itemHandler = entity.getCapability(ItemHandler.ENTITY_AUTOMATION, null); - } - - if (itemHandler != null) - { - deepExtractItemHandler(itemHandler, entityContent::add); - } - // some vanilla entities "have inventory" but not forge cap yet - else if (entity instanceof final ItemFrame itemFrame) - { - final ItemStack stack = itemFrame.getItem(); - entityContent.add(stack); - deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), entityContent::add); - } - else if (entity instanceof final ItemEntity itemEntity) - { - final ItemStack stack = itemEntity.getItem(); - entityContent.add(stack); - deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), entityContent::add); - } - else // sided item handler - { - for (final Direction side : Direction.values()) - { - final IItemHandler cap = entity.getCapability(ItemHandler.ENTITY_AUTOMATION, side); - if (cap != null) - { - deepExtractItemHandler(cap, entityContent::add); - } - } - } - - return entityContent; - } - - /** - * @return item that should spawn given entity - */ - @Nullable - public static ItemStack getEntitySpawningItem(final Entity entity) - { - if (entity instanceof final ItemFrame itemFrame) - { - return itemFrame.getFrameItemStack(); - } - return entity.getPickedResult(new EntityHitResult(entity)); - } - - /** - * Method to compare to stacks, ignoring their stacksize. - * - * @param itemStack1 The left stack to compare. - * @param itemStack2 The right stack to compare. - * @return True when they are equal except the stacksize, false when not. - */ - public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2) - { - return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, true, true); - } - - /** - * Method to compare to stacks, ignoring their stacksize. - * - * @param itemStack1 The left stack to compare. - * @param itemStack2 The right stack to compare. - * @param matchDamage Set to true to match damage data. - * @param matchNBT Set to true to match nbt - * @return True when they are equal except the stacksize, false when not. - */ - public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2, final boolean matchDamage, final boolean matchNBT) - { - return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, matchDamage, matchNBT, false); - } - - /** - * Method to compare to stacks, ignoring their stacksize. - * - * @param itemStack1 The left stack to compare. - * @param itemStack2 The right stack to compare. - * @param matchDamage Set to true to match damage data. - * @param matchNBT Set to true to match nbt - * @param min if the count of stack2 has to be at least the same as stack1. - * @return True when they are equal except the stacksize, false when not. - */ - public static boolean compareItemStacksIgnoreStackSize( - final ItemStack itemStack1, - final ItemStack itemStack2, - final boolean matchDamage, - final boolean matchNBT, - final boolean min) - { - if (isEmpty(itemStack1) && isEmpty(itemStack2)) - { - return true; - } - - if (isEmpty(itemStack1) != isEmpty(itemStack2)) - { - return false; - } - - if (itemStack1.getItem() == itemStack2.getItem() && (!matchDamage || itemStack1.getDamageValue() == itemStack2.getDamageValue())) - { - if (!matchNBT) - { - // Not comparing nbt - return true; - } - - if (min && itemStack1.getCount() > itemStack2.getCount()) - { - return false; - } - - // Then sort on NBT - if (!itemStack1.getComponents().isEmpty() && !itemStack2.getComponents().isEmpty()) - { - final DataComponentMap nbt1 = itemStack1.getComponents(); - final DataComponentMap nbt2 = itemStack2.getComponents(); - - for(final DataComponentType key : nbt1.keySet()) - { - if(!matchDamage && key == DataComponents.DAMAGE) - { - continue; - } - if(!nbt2.has(key) || !nbt1.get(key).equals(nbt2.get(key))) - { - return false; - } - } - - return nbt1.keySet().size() == nbt2.keySet().size(); - } - else - { - return itemStack1.getComponents().isEmpty() == itemStack2.getComponents().isEmpty(); - } - } - return false; - } - - /** - * Item serializer helper, including air - * - * @param stack - * @param buf - */ - public static void serializeToBuffer(final ItemStack stack, RegistryFriendlyByteBuf buf) - { - buf.writeBoolean(stack.isEmpty()); - if (!stack.isEmpty()) - { - ItemStack.STREAM_CODEC.encode(buf, stack); - } - } - - /** - * Item deserializer helper, including air. Must be serialized with the above util - * - * @param buf - */ - public static ItemStack deserializeFromBuffer(RegistryFriendlyByteBuf buf) - { - if (!buf.readBoolean()) - { - return ItemStack.STREAM_CODEC.decode(buf); - } - - return ItemStack.EMPTY; - } -} diff --git a/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java b/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java index c7f93b764f..7c73cf9bf9 100644 --- a/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java @@ -1,24 +1,28 @@ package com.ldtteam.structurize.blockentities; -import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.component.CapturedBlock; -import com.ldtteam.structurize.component.ModDataComponents; -import com.mojang.serialization.DynamicOps; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.component.DataComponentMap; -import net.minecraft.core.registries.BuiltInRegistries; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; +import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.RotationMirror; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; -import net.minecraft.nbt.Tag; +import net.minecraft.network.Connection; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -27,12 +31,6 @@ */ public class BlockEntityTagSubstitution extends BlockEntity implements IBlueprintDataProviderBE { - public static final String CAPTURED_BLOCK_TAG = "captured_block"; - /** - * Up to 1.21.1 - */ - public static final String CAPTURED_BLOCK_TAG_OLD = "replacement"; - /** * The schematic name of the block. */ @@ -62,7 +60,7 @@ public class BlockEntityTagSubstitution extends BlockEntity implements IBlueprin /** * Replacement block. */ - private CapturedBlock replacement = CapturedBlock.EMPTY; + private ReplacementBlock replacement = new ReplacementBlock(new CompoundTag()); public BlockEntityTagSubstitution(final BlockPos pos, final BlockState state) { @@ -122,58 +120,30 @@ public BlockPos getTilePos() * @return the replacement block details */ @NotNull - public CapturedBlock getReplacement() + public ReplacementBlock getReplacement() { return this.replacement; } @Override - public void loadAdditional( @NotNull final CompoundTag compound, final HolderLookup.Provider provider) - { - super.loadAdditional(compound, provider); - final DynamicOps dynamicOps = provider.createSerializationContext(NbtOps.INSTANCE); - - IBlueprintDataProviderBE.super.readSchematicDataFromNBT(compound); - if (compound.contains(CAPTURED_BLOCK_TAG_OLD, Tag.TAG_COMPOUND)) - { - final CompoundTag oldNbt = compound.getCompound(CAPTURED_BLOCK_TAG_OLD); - replacement = new CapturedBlock(NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), oldNbt.getCompound("b")), - Optional.of(oldNbt.getCompound("e")), - oldNbt.contains("i") ? ItemStack.parseOptional(provider, oldNbt.getCompound("i")) : ItemStack.EMPTY); - } - else - { - replacement = deserializeReplacement(compound, dynamicOps); - } - } - - public static CapturedBlock deserializeReplacement(final CompoundTag compound, final DynamicOps dynamicOps) - { - if (compound.getCompound(CAPTURED_BLOCK_TAG).isEmpty()) - { - return CapturedBlock.EMPTY; - } - return CapturedBlock.CODEC.parse(dynamicOps, compound.get(CAPTURED_BLOCK_TAG)).resultOrPartial(error -> { - Log.getLogger() - .error("Parsing {} with data {}: {}", ModBlockEntities.TAG_SUBSTITUTION.getRegisteredName(), compound, error); - Log.getLogger().error("", new RuntimeException()); - }).orElse(CapturedBlock.EMPTY); - } - - @Override - public void saveAdditional(@NotNull final CompoundTag compound, final HolderLookup.Provider provider) - { - super.saveAdditional(compound, provider); - final DynamicOps dynamicOps = provider.createSerializationContext(NbtOps.INSTANCE); - writeSchematicDataToNBT(compound); - - // this is still needed even with data components as of 1.21 - serializeReplacement(compound, dynamicOps, replacement); - } - - public static void serializeReplacement(final CompoundTag compound, final DynamicOps dynamicOps, final CapturedBlock replacement) - { - compound.put(CAPTURED_BLOCK_TAG, CapturedBlock.CODEC.encodeStart(dynamicOps, replacement).getOrThrow()); + protected void loadAdditional(@NotNull final ValueInput input) + { + super.loadAdditional(input); + input.read(IBlueprintDataProviderBE.TAG_BLUEPRINTDATA, CompoundTag.CODEC) + .ifPresent(IBlueprintDataProviderBE.super::readSchematicDataFromNBT); + this.replacement = input.read(ReplacementBlock.TAG_REPLACEMENT, CompoundTag.CODEC) + .map(ReplacementBlock::new) + .orElse(new ReplacementBlock()); + } + + @Override + protected void saveAdditional(@NotNull final ValueOutput output) + { + super.saveAdditional(output); + final CompoundTag schematicData = new CompoundTag(); + writeSchematicDataToNBT(schematicData); + output.store(IBlueprintDataProviderBE.TAG_BLUEPRINTDATA, CompoundTag.CODEC, schematicData); + output.store(ReplacementBlock.TAG_REPLACEMENT, CompoundTag.CODEC, this.replacement.write(new CompoundTag())); } @Override @@ -208,28 +178,226 @@ public String getBlueprintPath() @NotNull @Override - public CompoundTag getUpdateTag(final HolderLookup.Provider provider) - { - return saveCustomOnly(provider); + public CompoundTag getUpdateTag(final HolderLookup.Provider registries) + { + return saveWithFullMetadata(registries); + } + + @Override + public void onDataPacket(final Connection net, final ValueInput input) + { + loadAdditional(input); } - @Override - protected void applyImplicitComponents(final BlockEntity.DataComponentInput componentInput) - { - super.applyImplicitComponents(componentInput); - replacement = componentInput.getOrDefault(ModDataComponents.CAPTURED_BLOCK, CapturedBlock.EMPTY); - } + /** + * Storage for information about the replacement block, if any. + */ + public static class ReplacementBlock + { + private static final String TAG_REPLACEMENT = "replacement"; + + private final BlockState blockstate; + private final CompoundTag blockentitytag; + private final ItemStack itemstack; + + @Nullable private BlockEntity cachedBlockentity; + + private ReplacementBlock() + { + this.blockstate = net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); + this.blockentitytag = new CompoundTag(); + this.itemstack = ItemStack.EMPTY; + } + + /** + * Construct + * @param blockstate the block state + * @param blockentity the block entity, if any + * @param itemstack the item stack + */ + public ReplacementBlock(@NotNull final BlockState blockstate, + @Nullable final BlockEntity blockentity, + @NotNull final ItemStack itemstack) + { + this.blockstate = blockstate; + this.blockentitytag = blockentity == null + ? new CompoundTag() + : blockentity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + this.itemstack = itemstack; + } - @Override - protected void collectImplicitComponents(final DataComponentMap.Builder componentBuilder) - { - super.collectImplicitComponents(componentBuilder); - componentBuilder.set(ModDataComponents.CAPTURED_BLOCK, replacement); - } + /** + * Construct + * @param blockstate the block state + * @param blockentity the block entity tag, if any + * @param itemstack the item stack + */ + public ReplacementBlock(@NotNull final BlockState blockstate, + @Nullable final CompoundTag blockentity, + @NotNull final ItemStack itemstack) + { + this.blockstate = blockstate; + this.blockentitytag = blockentity == null ? new CompoundTag() : blockentity.copy(); + this.itemstack = itemstack; + } - @Override - public void removeComponentsFromTag(final CompoundTag itemStackTag) - { - itemStackTag.remove(CAPTURED_BLOCK_TAG); + /** + * Construct from tag + * @param tag the tag to load + */ + public ReplacementBlock(@NotNull CompoundTag tag) + { + final CompoundTag replacement = tag.getCompoundOrEmpty(TAG_REPLACEMENT); + this.blockstate = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, replacement.getCompoundOrEmpty("b")); + this.blockentitytag = replacement.getCompoundOrEmpty("e"); + this.itemstack = replacement.contains("i") + ? ItemStackUtils.getItemStackFromNbt(replacement.getCompoundOrEmpty("i")) + : ItemStack.EMPTY; + } + + /** + * @return true if there is no replacement block set (assume air) + */ + public boolean isEmpty() + { + return this.blockstate.isAir(); + } + + /** + * @return the block state + */ + @NotNull + public BlockState getBlockState() + { + return this.blockstate; + } + + /** + * @return the block entity tag + */ + @NotNull + public CompoundTag getBlockEntityTag() + { + return this.blockentitytag; + } + + /** + * @return the item stack + */ + @NotNull + public ItemStack getItemStack() + { + return this.itemstack; + } + + /** + * Creates and loads (once) the replacement block entity, or returns the preloaded one. + * @param pos the blockpos to use (ignored if already loaded) + * @return the new or cached entity, or null if there isn't one + */ + @Nullable + public BlockEntity getBlockEntity(final BlockPos pos) + { + if (this.cachedBlockentity == null) + { + this.cachedBlockentity = createBlockEntity(pos); + } + return this.cachedBlockentity; + } + + /** + * Always creates and loads a new replacement block entity, if needed. + * @param pos the blockpos to use + * @return the new entity, or null if there isn't one + */ + @Nullable + public BlockEntity createBlockEntity(final BlockPos pos) + { + return this.blockentitytag.isEmpty() + ? null + : BlockEntity.loadStatic( + pos, + this.blockstate, + this.blockentitytag, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + } + + /** + * Serialisation + * @param tag the target tag + * @return the target tag, for convenience + */ + @NotNull + public CompoundTag write(@NotNull CompoundTag tag) + { + if (isEmpty()) + { + tag.remove(TAG_REPLACEMENT); + } + else + { + final CompoundTag replacement = new CompoundTag(); + replacement.put("b", NbtUtils.writeBlockState(this.blockstate)); + if (this.blockentitytag.isEmpty()) + { + replacement.remove("e"); + } + else + { + replacement.put("e", this.blockentitytag); + } + replacement.put("i", ItemStackUtils.writeToNbt(this.itemstack)); + + tag.put(TAG_REPLACEMENT, replacement); + } + return tag; + } + + /** + * Creates a new single-block {@link Blueprint} for the replacement block. + * @return the blueprint + */ + @NotNull + public Blueprint createBlueprint() + { + final Blueprint blueprint = new Blueprint((short) 1, (short) 1, (short) 1); + blueprint.addBlockState(BlockPos.ZERO, getBlockState()); + blueprint.getTileEntities()[0][0][0] = getBlockEntityTag().isEmpty() ? null : getBlockEntityTag().copy(); + return blueprint; + } + + @NotNull + @Deprecated(since="1.20", forRemoval=true) + public BlockEntityTagSubstitution.ReplacementBlock rotateWithMirror(@NotNull final BlockPos pos, + @NotNull final Rotation localRotation, + @NotNull final Mirror localMirror, + @NotNull final Level world) + { + final Blueprint blueprint = createBlueprint(); + blueprint.rotateWithMirror(localRotation, localMirror, world); + + final BlockState newBlockState = blueprint.getBlockState(BlockPos.ZERO); + final CompoundTag newBlockData = blueprint.getTileEntityData(pos, BlockPos.ZERO); + return new ReplacementBlock(newBlockState, newBlockData, this.getItemStack()); + } + + /** + * Rotates and mirrors the replacement data, in response to a blueprint containing this replacement block + * being rotated or mirrored. + * + * @param pos the world location for the replacement block + * @param rotationMirror the relative rotation/mirror + * @param level the (actual) world + * @return the new replacement data + */ + public ReplacementBlock rotateWithMirror(final BlockPos pos, final RotationMirror rotationMirror, final Level level) + { + final Blueprint blueprint = createBlueprint(); + blueprint.setRotationMirrorRelative(rotationMirror, level); + + final BlockState newBlockState = blueprint.getBlockState(BlockPos.ZERO); + final CompoundTag newBlockData = blueprint.getTileEntityData(pos, BlockPos.ZERO); + return new ReplacementBlock(newBlockState, newBlockData, this.getItemStack()); + } } } diff --git a/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java b/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java index 1af95c8e20..4be69775f2 100644 --- a/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java +++ b/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java @@ -1,8 +1,9 @@ package com.ldtteam.structurize.blockentities; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.ModBlocks; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; @@ -11,8 +12,16 @@ public final class ModBlockEntities { private ModBlockEntities() { /* prevent construction */ } - public static final DeferredRegister> BLOCK_ENTITIES = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, Constants.MOD_ID); + private static final DeferredRegister> BLOCK_ENTITIES = + DeferredRegister.create(BuiltInRegistries.BLOCK_ENTITY_TYPE, Constants.MOD_ID); - public static DeferredHolder, BlockEntityType> TAG_SUBSTITUTION = BLOCK_ENTITIES.register("tagsubstitution", - () -> BlockEntityType.Builder.of(BlockEntityTagSubstitution::new, ModBlocks.blockTagSubstitution.get()).build(null)); + public static DeferredRegister> getRegistry() + { + return BLOCK_ENTITIES; + } + + public static DeferredHolder, BlockEntityType> TAG_SUBSTITUTION = + getRegistry().register( + "tagsubstitution", + () -> new BlockEntityType<>(BlockEntityTagSubstitution::new, new Block[] {ModBlocks.blockTagSubstitution.value()})); } diff --git a/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java b/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java index 3a9b0ae81c..365fc0191a 100644 --- a/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java +++ b/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java @@ -1,11 +1,11 @@ package com.ldtteam.structurize.blockentities.interfaces; -import com.ldtteam.structurize.api.BlockPosUtil; +import com.ldtteam.structurize.api.util.BlockPosUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.nbt.ListTag; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.core.BlockPos; import java.util.*; @@ -133,10 +133,10 @@ default void readSchematicDataFromNBT(final CompoundTag originalCompound) return; } - CompoundTag compoundNBT = originalCompound.getCompound(TAG_BLUEPRINTDATA); + CompoundTag compoundNBT = originalCompound.getCompoundOrEmpty(TAG_BLUEPRINTDATA); // Read schematic name - setSchematicName(compoundNBT.getString(TAG_SCHEMATIC_NAME)); + setSchematicName(compoundNBT.getStringOr(TAG_SCHEMATIC_NAME, "")); // Read corners final BlockPos corner1 = BlockPosUtil.readFromNBT(compoundNBT, TAG_CORNER_ONE); @@ -161,7 +161,7 @@ static Map> readTagPosMapFrom(final CompoundTag compoundN return tagPosMap; } - final ListTag tagPosMapNBT = compoundNBT.getList(TAG_POS_TAG_MAP, Tag.TAG_COMPOUND); + final ListTag tagPosMapNBT = compoundNBT.getListOrEmpty(TAG_POS_TAG_MAP); for (final Tag tagPosMapEntry : tagPosMapNBT) { @@ -174,7 +174,7 @@ static Map> readTagPosMapFrom(final CompoundTag compoundN final BlockPos tagPos = BlockPosUtil.readFromNBT(entry, TAG_TAG_POS); final Set tagList = new HashSet<>(); - final ListTag tagListNbt = entry.getList(TAG_TAG_NAME_LIST, Tag.TAG_COMPOUND); + final ListTag tagListNbt = entry.getListOrEmpty(TAG_TAG_NAME_LIST); for (final Tag tagEntryNBT : tagListNbt) { @@ -184,7 +184,7 @@ static Map> readTagPosMapFrom(final CompoundTag compoundN } final CompoundTag tagEntry = ((CompoundTag) tagEntryNBT); - tagList.add(tagEntry.getString(TAG_TAG_NAME)); + tagList.add(tagEntry.getStringOr(TAG_TAG_NAME, "")); } tagPosMap.put(tagPos, new ArrayList<>(tagList)); diff --git a/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java b/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java index a1bd09727a..f5787e3d12 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java +++ b/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java @@ -1,17 +1,21 @@ package com.ldtteam.structurize.blocks; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.schematic.BlockFluidSubstitution; import com.ldtteam.structurize.blocks.schematic.BlockSolidSubstitution; import com.ldtteam.structurize.blocks.schematic.BlockSubstitution; import com.ldtteam.structurize.blocks.schematic.BlockTagSubstitution; import com.ldtteam.structurize.items.ModItems; +import net.minecraft.resources.Identifier; import net.minecraft.tags.BlockTags; import net.minecraft.tags.TagKey; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; -import java.util.function.Supplier; /** * Class to register blocks to Structurize @@ -20,11 +24,17 @@ public final class ModBlocks { private ModBlocks() { /* prevent construction */ } - public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Constants.MOD_ID); + private static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Constants.MOD_ID); - public static final TagKey NULL_PLACEMENT = BlockTags.create(Constants.resLocStruct("null_placement")); + public static DeferredRegister.Blocks getRegistry() + { + return BLOCKS; + } + + public static final TagKey NULL_PLACEMENT = + BlockTags.create(Identifier.fromNamespaceAndPath("structurize", "null_placement")); - public static final DeferredBlock blockSubstitution; + public static final DeferredBlock blockSubstitution; public static final DeferredBlock blockSolidSubstitution; public static final DeferredBlock blockFluidSubstitution; public static final DeferredBlock blockTagSubstitution; @@ -36,10 +46,14 @@ private ModBlocks() { /* prevent construction */ } * @param the block subclass for the factory response * @return the block entry saved to the registry */ - public static DeferredBlock registerWithBlockItem(String name, Supplier block) + public static DeferredBlock register(String name, java.util.function.Function block) { - final DeferredBlock registered = BLOCKS.register(name, block); - ModItems.ITEMS.registerSimpleBlockItem(registered); + final DeferredBlock registered = + BLOCKS.registerBlock(name.toLowerCase(), block, () -> BlockBehaviour.Properties.of()); + // DeferredRegister.Blocks only registers the block. Keep the + // corresponding BlockItem registration explicit so recipes, creative + // tabs, and ItemStack lookups all resolve the same id. + ModItems.getRegistry().registerSimpleBlockItem(name.toLowerCase(), registered::get); return registered; } @@ -49,9 +63,12 @@ public static DeferredBlock registerWithBlockItem(String na static { - blockSubstitution = registerWithBlockItem("blockSubstitution".toLowerCase(), BlockSubstitution::new); - blockSolidSubstitution = registerWithBlockItem("blockSolidSubstitution".toLowerCase(), BlockSolidSubstitution::new); - blockFluidSubstitution = registerWithBlockItem("blockFluidSubstitution".toLowerCase(), BlockFluidSubstitution::new); - blockTagSubstitution = BLOCKS.register("blockTagSubstitution".toLowerCase(), BlockTagSubstitution::new); + blockSubstitution = register("blockSubstitution", BlockSubstitution::new); + blockSolidSubstitution = register("blockSolidSubstitution", BlockSolidSubstitution::new); + blockFluidSubstitution = register("blockFluidSubstitution", BlockFluidSubstitution::new); + // ItemTagSubstitution is the custom BlockItem for this block and is + // registered by ModItems, so do not create a duplicate simple item. + blockTagSubstitution = BLOCKS.registerBlock("blocktagsubstitution", BlockTagSubstitution::new, + () -> BlockBehaviour.Properties.of()); } } diff --git a/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java b/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java index 81f82c4bff..a09fe8e1ee 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java +++ b/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java @@ -1,9 +1,9 @@ package com.ldtteam.structurize.blocks.interfaces; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import java.util.List; @@ -16,7 +16,7 @@ public interface IRequirementsBlueprintAnchorBlock * Get the list of requirements as chat components. * @return List for display. */ - List getRequirements(final ClientLevel level, final BlockPos pos, final LocalPlayer player); + List getRequirements(final Level level, final BlockPos pos, final Player player); /** * Check if the requirements are met for: @@ -25,5 +25,5 @@ public interface IRequirementsBlueprintAnchorBlock * @param player the player. * @return true if so. */ - boolean areRequirementsMet(final ClientLevel level, final BlockPos pos, final LocalPlayer player); + boolean areRequirementsMet(final Level level, final BlockPos pos, final Player player); } diff --git a/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java b/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java index b1c7751a5a..43aafe8100 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java +++ b/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java @@ -2,7 +2,7 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.placement.structure.AbstractStructureHandler; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; @@ -15,7 +15,7 @@ public interface ISpecialCreativeHandlerAnchorBlock /** * Get the special structure handler. */ - AbstractStructureHandler getStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final RotationMirror rotMir, final boolean fancyPlacement); + AbstractStructureHandler getStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final PlacementSettings settings, final boolean fancyPlacement); /** * Pre-setup before running the placement. @@ -24,7 +24,7 @@ public interface ISpecialCreativeHandlerAnchorBlock * @param world the world to run it for. * @param pos the pos to run it at. * @param blueprint the matching blueprint. - * @param rotMir the settings (mirror and rotation). + * @param settings the settings (mirror and rotation). * @param fancyPlacement if constructed or creative * @param pack the pack name. * @param path the path within the pack. @@ -35,7 +35,7 @@ boolean setup( final Level world, final BlockPos pos, final Blueprint blueprint, - final RotationMirror rotMir, + final PlacementSettings settings, final boolean fancyPlacement, final String pack, final String path); diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java index 06af827d73..672795b00c 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.blocks.schematic; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour.Properties; /** * This block is used as a substitution block for the Builder. @@ -13,8 +14,8 @@ public class BlockFluidSubstitution extends Block * Constructor for the Substitution block. * sets the creative tab, as well as the resistance and the hardness. */ - public BlockFluidSubstitution() + public BlockFluidSubstitution(final Properties properties) { - super(BlockSubstitution.defaultSubstitutionProperties()); + super(BlockSubstitution.defaultSubstitutionProperties(properties)); } } diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java index ab5b4af69e..a633b5771e 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.blocks.schematic; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour.Properties; /** * This block is used as a substitution block for the Builder. @@ -13,8 +14,8 @@ public class BlockSolidSubstitution extends Block * Constructor for the Substitution block. * sets the creative tab, as well as the resistance and the hardness. */ - public BlockSolidSubstitution() + public BlockSolidSubstitution(final Properties properties) { - super(BlockSubstitution.defaultSubstitutionProperties()); + super(BlockSubstitution.defaultSubstitutionProperties(properties)); } } diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java index 6abc7ec8d1..4c3bcb6452 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java @@ -2,6 +2,7 @@ import com.ldtteam.structurize.items.ModItems; import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.LevelAccessor; @@ -17,7 +18,8 @@ import net.minecraft.world.phys.shapes.EntityCollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import org.jetbrains.annotations.Nullable; + +import javax.annotation.Nullable; /** * This block is used as a substitution block for the Builder. Every solid block can be substituted by this block in schematics. This helps make schematics independent from @@ -28,15 +30,20 @@ public class BlockSubstitution extends Block implements LiquidBlockContainer /** * Constructor for the Substitution block. sets the creative tab, as well as the resistance and the hardness. */ - public BlockSubstitution() + public BlockSubstitution(final Properties properties) { - super(defaultSubstitutionProperties() + super(defaultSubstitutionProperties(properties) .forceSolidOff()); // don't kill farmland and path blocks underneath } public static Properties defaultSubstitutionProperties() { - return Properties.of() + return defaultSubstitutionProperties(Properties.of()); + } + + public static Properties defaultSubstitutionProperties(final Properties properties) + { + return properties .mapColor(MapColor.WOOD) .sound(SoundType.WOOD) .instabreak() // must be before explosionResistance @@ -72,14 +79,19 @@ public VoxelShape getBlockSupportShape(BlockState state, BlockGetter worldIn, Bl } @Override - public boolean canPlaceLiquid(@Nullable Player player, BlockGetter level, BlockPos pos, BlockState state, Fluid fluid) + public boolean canPlaceLiquid( + final @Nullable LivingEntity entity, + final BlockGetter worldIn, + final BlockPos pos, + final BlockState state, + final Fluid fluid) { // Don't allow water to flow inside despite being non-solid return false; } @Override - public boolean placeLiquid(LevelAccessor level, BlockPos pos, BlockState state, FluidState fluidState) + public boolean placeLiquid(LevelAccessor worldIn, BlockPos pos, BlockState state, FluidState fluid) { return false; } diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java index 979d8570fa..96d0f4b7e7 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java @@ -1,23 +1,32 @@ package com.ldtteam.structurize.blocks.schematic; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; -import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.LevelReader; +import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.HitResult; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.common.extensions.IBlockExtension; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; /** * This block is a substitution block (it disappears on normal build) but stores blueprint data (mostly tags) during scan. */ -public class BlockTagSubstitution extends BlockSubstitution implements IAnchorBlock, EntityBlock -{ +public class BlockTagSubstitution extends BlockSubstitution implements IAnchorBlock, EntityBlock +{ + public BlockTagSubstitution(final net.minecraft.world.level.block.state.BlockBehaviour.Properties properties) + { + super(properties); + } + @Nullable @Override public BlockEntity newBlockEntity(final @NotNull BlockPos blockPos, final @NotNull BlockState blockState) @@ -27,26 +36,26 @@ public BlockEntity newBlockEntity(final @NotNull BlockPos blockPos, final @NotNu @NotNull @Override - @SuppressWarnings("deprecation") - public ItemStack getCloneItemStack(@NotNull final LevelReader level, - @NotNull final BlockPos pos, - @NotNull final BlockState blockState) - { - return cloneItemStack(super.getCloneItemStack(level, pos, blockState), level, pos); - } - - @Override - public ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, Player player) - { - return cloneItemStack(super.getCloneItemStack(state, target, level, pos, player), level, pos); - } - - private ItemStack cloneItemStack(final ItemStack stack, LevelReader level, BlockPos pos) - { - if (level.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution entity) - { - entity.saveToItem(stack, level.registryAccess()); - } - return stack; - } -} + public ItemStack getCloneItemStack(@NotNull final LevelReader level, + @NotNull final BlockPos pos, + @NotNull final BlockState blockState, + final boolean includeNonCreative, + @Nullable final Player player) + { + return cloneItemStack( + super.getCloneItemStack(level, pos, blockState, includeNonCreative, player), + level, + pos); + } + + private ItemStack cloneItemStack(final ItemStack stack, LevelReader level, BlockPos pos) + { + if (level.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution entity) + { + stack.set( + DataComponents.CUSTOM_DATA, + CustomData.of(entity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)))); + } + return stack; + } +} diff --git a/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java b/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java index 9d97f870da..72119e1d1c 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java @@ -2,9 +2,10 @@ import net.minecraft.core.Direction; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.block.GlazedTerracottaBlock; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; import java.util.ArrayList; import java.util.List; import java.util.function.Function; @@ -21,19 +22,19 @@ * Used during mirroring of blueprint palette */ -public record FacingFixer(Predicate test, DirectionProperty property, Function mapping) + public record FacingFixer(Predicate test, EnumProperty property, Function mapping) { public static final List MIRROR_FIXERS = new ArrayList<>(); - public static final FacingFixer GLAZED_TERRACOTA_SPECIAL = mirrorFixer(bs -> bs.getBlock() == Blocks.LIGHT_GRAY_GLAZED_TERRACOTTA || - bs.getBlock() == Blocks.PINK_GLAZED_TERRACOTTA || - bs.getBlock() == Blocks.BLUE_GLAZED_TERRACOTTA || - bs.getBlock() == Blocks.CYAN_GLAZED_TERRACOTTA, + public static final FacingFixer GLAZED_TERRACOTA_SPECIAL = mirrorFixer(bs -> bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.LIGHT_GRAY) || + bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.PINK) || + bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.BLUE) || + bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.CYAN), GlazedTerracottaBlock.FACING, FacingMapping.SOUTH_EAST_AND_NORTH_WEST); public static final FacingFixer GLAZED_TERRACOTA_MAJORITY = mirrorFixer(bs -> bs.getBlock() instanceof GlazedTerracottaBlock && - bs.getBlock() != Blocks.MAGENTA_GLAZED_TERRACOTTA, + bs.getBlock() != Blocks.GLAZED_TERRACOTTA.pick(DyeColor.MAGENTA), GlazedTerracottaBlock.FACING, FacingMapping.NORTH_EAST_AND_SOUTH_WEST); @@ -44,7 +45,9 @@ public record FacingFixer(Predicate test, DirectionProperty property * @return fixer registered as mirror fixer * @see #MIRROR_FIXERS */ - public static FacingFixer mirrorFixer(final Predicate test, final DirectionProperty property, final Function mapping) + public static FacingFixer mirrorFixer(final Predicate test, + final EnumProperty property, + final Function mapping) { final FacingFixer result = new FacingFixer(test, property, mapping); MIRROR_FIXERS.add(result); diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java index 5cd3feaee2..284a9b37df 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java @@ -1,35 +1,35 @@ package com.ldtteam.structurize.blueprints.v1; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.common.fakelevel.IFakeLevelBlockGetter; -import com.ldtteam.common.util.BlockToItemHelper; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blockentities.ModBlockEntities; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; -import com.ldtteam.structurize.component.CapturedBlock; +import com.ldtteam.structurize.blueprints.FacingFixer; +import com.ldtteam.structurize.client.fakelevel.IFakeLevelBlockGetter; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.BlockUtils; +import com.ldtteam.structurize.util.EntityNbtHelper; import com.ldtteam.structurize.util.BlueprintPositionInfo; -import com.mojang.serialization.DynamicOps; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.CrashReportCategory; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.decoration.HangingEntity; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; @@ -135,8 +135,6 @@ public class Blueprint implements IFakeLevelBlockGetter */ private RotationMirror rotationMirror = RotationMirror.NONE; - private final HolderLookup.Provider registryAccess; - /** * Constructor of a new Blueprint. * @@ -157,10 +155,8 @@ public Blueprint( List pallete, short[][][] structure, CompoundTag[] tileEntities, - List requiredMods, - HolderLookup.Provider registryAccess) + List requiredMods) { - this.registryAccess = registryAccess; this.sizeX = sizeX; this.sizeY = sizeY; this.sizeZ = sizeZ; @@ -173,7 +169,7 @@ public Blueprint( { if (te != null) { - this.tileEntities[te.getShort("y")][te.getShort("z")][te.getShort("x")] = te; + this.tileEntities[te.getShortOr("y", (short) 0)][te.getShortOr("z", (short) 0)][te.getShortOr("x", (short) 0)] = te; } } this.requiredMods = requiredMods; @@ -186,9 +182,8 @@ public Blueprint( * @param sizeY the y size. * @param sizeZ the z size. */ - public Blueprint(short sizeX, short sizeY, short sizeZ, HolderLookup.Provider registryAccess) + public Blueprint(short sizeX, short sizeY, short sizeZ) { - this.registryAccess = registryAccess; this.sizeX = sizeX; this.sizeY = sizeY; this.sizeZ = sizeZ; @@ -204,7 +199,7 @@ public Blueprint(short sizeX, short sizeY, short sizeZ, HolderLookup.Provider re /** * @return the Size of the Structure on the X-Axis (without rotation and/or mirroring) */ - public int getSizeX() + public short getSizeX() { return this.sizeX; } @@ -220,7 +215,7 @@ public short getSizeY() /** * @return the Size of the Structure on the Z-Axis (without rotation and/or mirroring) */ - public int getSizeZ() + public short getSizeZ() { return this.sizeZ; } @@ -518,10 +513,8 @@ public CompoundTag getTileEntityData(final BlockPos worldPos, final BlockPos str * * @param pos the pos its at. * @return an item or null if not initialized. - * @deprecated use {@link BlockToItemHelper} */ @Nullable - @Deprecated(forRemoval = true, since = "1.21.1") public Item getItem(final BlockPos pos) { @Nullable @@ -650,16 +643,11 @@ public void setRotationMirror(final RotationMirror rotationMirror, final Level l */ public void setRotationMirrorRelative(final RotationMirror transformBy, final Level level) { - if (level.registryAccess() != registryAccess) - { - throw new IllegalStateException("World mismatch"); - } if (transformBy == RotationMirror.NONE) { return; } - final DynamicOps dynamicNbtOps = registryAccess.createSerializationContext(NbtOps.INSTANCE); final BlockPos primaryOffset = getPrimaryBlockOffset(); final short newSizeX, newSizeZ, newSizeY = sizeY; @@ -684,7 +672,14 @@ public void setRotationMirrorRelative(final RotationMirror transformBy, final Le final List palette = new ArrayList<>(); for (int i = 0; i < this.palette.size(); i++) { - palette.add(i, transformBy.applyToBlockState(this.palette.get(i))); + BlockState bs = this.palette.get(i); + + if (transformBy.isMirrored()) + { + bs = FacingFixer.fixMirroredFacing(bs.mirror(transformBy.mirror()), bs); + } + + palette.add(i, bs.rotate(transformBy.rotation())); } final BlockPos extremes = transformBy.applyToPos(new BlockPos(sizeX, sizeY, sizeZ)); @@ -721,16 +716,17 @@ public void setRotationMirrorRelative(final RotationMirror transformBy, final Le // Level with blockstate and entity and the former requires reinflating everything // before we can test whether it's rotatable or not, neither of which is ideal. So // for now this is the minimal requirement. - if (compound.getString("id").equals(ModBlockEntities.TAG_SUBSTITUTION.getId().toString())) + if (compound.getStringOr("id", "").equals(ModBlockEntities.TAG_SUBSTITUTION.getId().toString())) { - CapturedBlock replacement = BlockEntityTagSubstitution.deserializeReplacement(compound, dynamicNbtOps); - replacement = replacement.applyRotationMirror(transformBy, level); - BlockEntityTagSubstitution.serializeReplacement(compound, dynamicNbtOps, replacement); + BlockEntityTagSubstitution.ReplacementBlock replacement = + new BlockEntityTagSubstitution.ReplacementBlock(compound); + replacement = replacement.rotateWithMirror(tempPos, transformBy, level); + replacement.write(compound); } if (compound.contains(TAG_BLUEPRINTDATA)) { - CompoundTag dataCompound = compound.getCompound(TAG_BLUEPRINTDATA); + CompoundTag dataCompound = compound.getCompoundOrEmpty(TAG_BLUEPRINTDATA); // Rotate tag map final Map> tagPosMap = IBlueprintDataProviderBE.readTagPosMapFrom(dataCompound); @@ -776,6 +772,20 @@ public void setRotationMirrorRelative(final RotationMirror transformBy, final Le cacheReset(false); } + /** + * Rotate the structure depending on the direction it's facing. + * + * @param localRotation times to rotateWithMirror. + * @param localMirror the mirror. + * @param world the world. + * @deprecated replaced by {@link #setRotationMirrorRelative(RotationMirror, Level)} or use exact setter {@link #setRotationMirror(RotationMirror, Level)} + */ + @Deprecated(since="1.20", forRemoval=true) + public void rotateWithMirror(final Rotation localRotation, final Mirror localMirror, final Level world) + { + setRotationMirrorRelative(RotationMirror.of(localRotation, localMirror), world); + } + /** * Transform an entity and rotate it. * @@ -790,39 +800,36 @@ private static CompoundTag transformEntityInfoWithSettings(final CompoundTag ent final BlockPos pos, final RotationMirror rotationMirror) { - final Optional> type = EntityType.by(entityInfo); - if (type.isPresent()) - { - final Entity finalEntity = type.get().create(world); + final Entity finalEntity = EntityType.loadEntityRecursive( + entityInfo, + world, + new EntitySpawnRequest(EntitySpawnReason.LOAD, false), + loaded -> loaded); - if (finalEntity != null) + if (finalEntity != null) + { + try { - try - { - finalEntity.load(entityInfo); - - final Vec3 entityVec = rotationMirror - .applyToPos( - finalEntity instanceof HangingEntity hang ? Vec3.atCenterOf(hang.getPos()) : finalEntity.position()) - .add(Vec3.atLowerCornerOf(pos)); - finalEntity.setYRot(finalEntity.mirror(rotationMirror.mirror())); - finalEntity.setYRot(finalEntity.rotate(rotationMirror.rotation())); - finalEntity.moveTo(entityVec.x, entityVec.y, entityVec.z, finalEntity.getYRot(), finalEntity.getXRot()); - - final CompoundTag newEntityInfo = new CompoundTag(); - finalEntity.save(newEntityInfo); - return newEntityInfo; - } - catch (final Exception ex) - { - Log.getLogger().error("Entity: " + type.get().getDescriptionId() + " failed to load. ", ex); - return null; - } + final Vec3 entityVec = rotationMirror + .applyToPos( + finalEntity instanceof HangingEntity hang ? Vec3.atCenterOf(hang.getPos()) : finalEntity.position()) + .add(Vec3.atLowerCornerOf(pos)); + finalEntity.setYRot(finalEntity.mirror(rotationMirror.mirror())); + finalEntity.setYRot(finalEntity.rotate(rotationMirror.rotation())); + finalEntity.setPos(entityVec.x, entityVec.y, entityVec.z); + + return EntityNbtHelper.save(finalEntity, world.registryAccess()); + } + catch (final Exception ex) + { + Log.getLogger().error("Entity: " + finalEntity.getType().getDescriptionId() + " failed to load. ", ex); + return null; } } return null; } + private int getVolume() { return (int) sizeX * sizeY * sizeZ; @@ -867,6 +874,18 @@ public boolean equals(final Object obj) // rot/mir intentionally not incluced } + /** + * Set the render source of the blueprint. + * This will be included in the hash to differentiate. + * This is supposed to be used for static blueprints that are not moved around only. + * @param pos the source position. + * @deprecated ask Ray what to use :) + */ + @Deprecated(since = "1.20", forRemoval = true) + public void setRenderSource(final BlockPos pos) + { + } + /** * Get blueprint info at position. * @@ -890,10 +909,10 @@ public BlueprintPositionInfo getBluePrintPositionInfo(final BlockPos pos, final */ private static boolean isAtPos(final CompoundTag entityData, final BlockPos pos) { - final ListTag list = entityData.getList(ENTITY_POS, 6); - final int x = (int) list.getDouble(0); - final int y = (int) list.getDouble(1); - final int z = (int) list.getDouble(2); + final ListTag list = entityData.getListOrEmpty(ENTITY_POS); + final int x = (int) list.getDoubleOr(0, 0D); + final int y = (int) list.getDoubleOr(1, 0D); + final int z = (int) list.getDoubleOr(2, 0D); return new BlockPos(x, y, z).equals(pos); } @@ -936,7 +955,7 @@ public String toString() @javax.annotation.Nullable public BlockEntity getBlockEntity(final BlockPos pos) { - return BlueprintUtils.constructTileEntity(getBlockInfoAsMap().get(pos), null, registryAccess); + return BlueprintUtils.constructTileEntity(getBlockInfoAsMap().get(pos), null); } @Override @@ -956,9 +975,4 @@ public void describeSelfInCrashReport(final CrashReportCategory category) category.setDetail("Blueprint size", () -> "%d %d %d".formatted(sizeX, sizeY, sizeZ)); category.setDetail("Blueprint rotation mirror", () -> rotationMirror.name()); } - - public HolderLookup.Provider getRegistryAccess() - { - return registryAccess; - } } diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java index 8f9dc48331..2c8df80288 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java @@ -14,8 +14,8 @@ import java.util.List; import java.util.Map; -import static com.ldtteam.structurize.api.constants.Constants.GROUNDLEVEL_TAG; -import static com.ldtteam.structurize.api.constants.Constants.INVISIBLE_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.GROUNDLEVEL_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.INVISIBLE_TAG; import static com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE.TAG_BLUEPRINTDATA; /** @@ -36,7 +36,7 @@ public static Map> getBlueprintTags(final Blueprint bluep if (nbt != null) { - return IBlueprintDataProviderBE.readTagPosMapFrom(nbt.getCompound(TAG_BLUEPRINTDATA)); + return IBlueprintDataProviderBE.readTagPosMapFrom(nbt.getCompoundOrEmpty(TAG_BLUEPRINTDATA)); } return new HashMap<>(); diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java index df25021644..dece4832b7 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java @@ -1,23 +1,23 @@ package com.ldtteam.structurize.blueprints.v1; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; +import com.ldtteam.structurize.util.EntityNbtHelper; import com.ldtteam.structurize.tag.ModTags; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.decoration.BlockAttachedEntity; +import net.minecraft.world.entity.decoration.HangingEntity; import net.minecraft.nbt.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.SharedConstants; import net.minecraft.util.datafix.fixes.References; -import net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix; import net.minecraft.world.phys.AABB; +import net.minecraft.core.RegistryAccess; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.Level; import net.minecraft.server.level.ServerLevel; @@ -32,7 +32,7 @@ import static com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE.*; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * @see Blueprint V1 Specification @@ -102,7 +102,7 @@ public static Blueprint createBlueprint( final BlockEntity te = chunk.getBlockEntities().containsKey(mutablePos) && !chunk.getBlockEntities().get(mutablePos).isRemoved() ? chunk.getBlockEntity(mutablePos) : world.getBlockEntity(mutablePos.immutable()); if (te != null) { - CompoundTag teTag = te.saveWithFullMetadata(world.registryAccess()); + CompoundTag teTag = te.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); teTag.putShort("x", x); teTag.putShort("y", y); teTag.putShort("z", z); @@ -134,26 +134,26 @@ public static Blueprint createBlueprint( } final Vec3 oldPos = entity.position(); - final CompoundTag entityTag = new CompoundTag(); - entity.save(entityTag); + final CompoundTag entityTag = EntityNbtHelper.save(entity, world.registryAccess()); final ListTag posList = new ListTag(); posList.add(DoubleTag.valueOf(oldPos.x - pos.getX())); posList.add(DoubleTag.valueOf(oldPos.y - pos.getY())); posList.add(DoubleTag.valueOf(oldPos.z - pos.getZ())); - if (entity instanceof final BlockAttachedEntity attachedEntity) + BlockPos entityPos = entity.blockPosition(); + if (entity instanceof HangingEntity) { - final BlockPos entityPos = attachedEntity.getPos(); - entityTag.put("TileX", IntTag.valueOf(entityPos.getX() - pos.getX())); - entityTag.put("TileY", IntTag.valueOf(entityPos.getY() - pos.getY())); - entityTag.put("TileZ", IntTag.valueOf(entityPos.getZ() - pos.getZ())); + entityPos = ((HangingEntity) entity).getPos(); } entityTag.put("Pos", posList); + entityTag.put("TileX", IntTag.valueOf(entityPos.getX() - pos.getX())); + entityTag.put("TileY", IntTag.valueOf(entityPos.getY() - pos.getY())); + entityTag.put("TileZ", IntTag.valueOf(entityPos.getZ() - pos.getZ())); entitiesTag.add(entityTag); } - final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) pallete.size(), pallete, structure, tes, requiredMods, world.registryAccess()); + final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) pallete.size(), pallete, structure, tes, requiredMods); schem.setEntities(entitiesTag.toArray(new CompoundTag[0])); if (anchorPos.isPresent()) @@ -183,7 +183,7 @@ public static Blueprint createBlueprint( BlockPosUtil.writeToNBT(blueprintData, TAG_CORNER_ONE, corner1); BlockPosUtil.writeToNBT(blueprintData, TAG_CORNER_TWO, corner2); - if (!world.isClientSide) + if (!world.isClientSide()) { ((ServerLevel) world).getChunkSource().blockChanged(pos); } @@ -209,9 +209,9 @@ public static CompoundTag writeBlueprintToNBT(final Blueprint schem) // Set Blueprint Version tag.putByte("version", (byte) 1); // Set Blueprint Size - tag.putShort("size_x", (short) schem.getSizeX()); + tag.putShort("size_x", schem.getSizeX()); tag.putShort("size_y", schem.getSizeY()); - tag.putShort("size_z", (short) schem.getSizeZ()); + tag.putShort("size_z", schem.getSizeZ()); // Create Pallete final BlockState[] palette = schem.getPalette(); @@ -269,7 +269,7 @@ public static CompoundTag writeBlueprintToNBT(final Blueprint schem) tag.put("architects", architectsTag); } - tag.put("mcversion", IntTag.valueOf(SharedConstants.getCurrentVersion().getDataVersion().getVersion())); + tag.put("mcversion", IntTag.valueOf(SharedConstants.getCurrentVersion().dataVersion().version())); final CompoundTag optionalTag = new CompoundTag(); final CompoundTag structurizeTag = new CompoundTag(); @@ -289,7 +289,7 @@ public static List fixPalette(final int oldDataVersion, final ListTa for (short i = 0; i < paletteSize; i++) { - final CompoundTag nbt = paletteTag.getCompound(i); + final CompoundTag nbt = paletteTag.getCompoundOrEmpty(i); try { final CompoundTag fixedNbt = DataFixerUtils.runDataFixer(nbt, References.BLOCK_STATE, oldDataVersion); @@ -303,7 +303,7 @@ public static List fixPalette(final int oldDataVersion, final ListTa break; } - final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), fixedNbt); + final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, fixedNbt); palette.add(i, state); } catch (final Exception e) @@ -318,8 +318,8 @@ public static List fixPalette(final int oldDataVersion, final ListTa private static void fixPalette1343(final CompoundTag oldBlockState) { - final String name = oldBlockState.getString("Name"); - oldBlockState.putString("Name", oldBlockState.getString("Name").toLowerCase(Locale.US)); + final String name = oldBlockState.getStringOr("Name", ""); + oldBlockState.putString("Name", oldBlockState.getStringOr("Name", "").toLowerCase(Locale.US)); if (name.contains(MOD_ID)) { if (name.contains("blockshingle_")) @@ -353,7 +353,7 @@ else if (name.contains("blocktimberframe")) else if (name.contains("blockpaperwall") && !name.contains("_")) { oldBlockState.putString("Name", - "structurize:" + oldBlockState.getCompound("Properties").getString("variant") + "_blockpaperwall"); + "structurize:" + oldBlockState.getCompoundOrEmpty("Properties").getStringOr("variant", "") + "_blockpaperwall"); } } } @@ -364,16 +364,16 @@ public static CompoundTag[] fixTileEntities(final int oldDataVersion, final List for (short i = 0; i < tileEntities.length; i++) { - final CompoundTag nbt = tileEntitiesTag.getCompound(i); + final CompoundTag nbt = tileEntitiesTag.getCompoundOrEmpty(i); try { - final String id = nbt.getString("id"); + final String id = nbt.getStringOr("id", ""); if (id.contains("minecolonies")) { nbt.putString("id", id.toLowerCase(Locale.US)); - nbt.putString("Item", nbt.getString("Item".toLowerCase(Locale.US))); + nbt.putString("Item", nbt.getStringOr("Item", "").toLowerCase(Locale.US)); tileEntities[i] = nbt; continue; } @@ -404,11 +404,11 @@ public static CompoundTag[] fixEntities(final int oldDataVersion, final ListTag for (short i = 0; i < entities.length; i++) { - final CompoundTag nbt = entitiesTag.getCompound(i); + final CompoundTag nbt = entitiesTag.getCompoundOrEmpty(i); try { - final String id = nbt.getString("id"); + final String id = nbt.getStringOr("id", ""); entities[i] = id.startsWith("minecraft:") ? DataFixerUtils.runDataFixer(nbt, References.ENTITY, oldDataVersion) : nbt; } @@ -452,7 +452,7 @@ private static Map searchForTEposInTEs(final List b final CompoundTag compound = tileEntities[i]; if (compound != null) { - final BlockPos bp = new BlockPos(compound.getInt("x"), compound.getInt("y"), compound.getInt("z")); + final BlockPos bp = new BlockPos(compound.getIntOr("x", 0), compound.getIntOr("y", 0), compound.getIntOr("z", 0)); if (blockPosToFind.contains(bp)) { result.put(i, bp); @@ -479,7 +479,7 @@ private static void teToBlockStateFix( final CompoundTag teCompound = tileEntities[e.getKey()]; tileEntities[e.getKey()] = null; final CompoundTag newBScompound = dataFixer.apply(teCompound); - final BlockState newBlockState = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), newBScompound); + final BlockState newBlockState = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, newBScompound); final short newBlockId = paletteFull ? newBlocksToBlockId.getOrDefault(newBlockState, (short) palette.size()) : paletteIndex; if (newBlockId == palette.size()) { @@ -509,19 +509,19 @@ public static void fixCross1343( if (bs.getBlock() == Blocks.POTTED_CACTUS) // flower pot fix { teToBlockStateFix(palette, blocks, tileEntities, i, teCompound -> { - final String type = teCompound.getString("Item") + teCompound.getInt("Data"); - return (CompoundTag) ChunkPalettedStorageFix.FLOWER_POT_MAP - .getOrDefault(type, ChunkPalettedStorageFix.FLOWER_POT_MAP.get("minecraft:air0")) + final String type = teCompound.getStringOr("Item", "") + teCompound.getIntOr("Data", 0); + return (CompoundTag) DataFixerUtils.FLOWER_POT_MAP + .getOrDefault(type, DataFixerUtils.FLOWER_POT_MAP.get("minecraft:air0")) .getValue(); }); } else if (bs.getBlock() == Blocks.NOTE_BLOCK) // note block fix { teToBlockStateFix(palette, blocks, tileEntities, i, teCompound -> { - final String type = Boolean.toString(teCompound.getBoolean("powered")) - + (byte) Math.min(Math.max(teCompound.getInt("note"), 0), 24); - return (CompoundTag) ChunkPalettedStorageFix.NOTE_BLOCK_MAP - .getOrDefault(type, ChunkPalettedStorageFix.NOTE_BLOCK_MAP.get("false0")) + final String type = Boolean.toString(teCompound.getBooleanOr("powered", false)) + + (byte) Math.min(Math.max(teCompound.getIntOr("note", 0), 0), 24); + return (CompoundTag) DataFixerUtils.NOTE_BLOCK_MAP + .getOrDefault(type, DataFixerUtils.NOTE_BLOCK_MAP.get("false0")) .getValue(); }); } @@ -534,13 +534,13 @@ else if (bs.getBlock() == Blocks.NOTE_BLOCK) // note block fix * @param nbtTag The CompoundNBT containing the Blueprint Data * @return A desserialized Blueprint */ - public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final HolderLookup.Provider provider) + public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag) { final CompoundTag tag = nbtTag; - byte version = tag.getByte("version"); + byte version = tag.getByteOr("version", (byte) 0); if (version == 1) { - short sizeX = tag.getShort("size_x"), sizeY = tag.getShort("size_y"), sizeZ = tag.getShort("size_z"); + short sizeX = tag.getShortOr("size_x", (short) 0), sizeY = tag.getShortOr("size_y", (short) 0), sizeZ = tag.getShortOr("size_z", (short) 0); // Reading required Mods List requiredMods = new ArrayList<>(); @@ -549,7 +549,7 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol short modListSize = (short) modsList.size(); for (int i = 0; i < modListSize; i++) { - requiredMods.add((modsList.get(i)).getAsString()); + requiredMods.add(modsList.getStringOr(i, "")); if (!requiredMods.get(i).equals("minecraft") && !ModList.get().getModContainerById(requiredMods.get(i)).isPresent()) { LogManager.getLogger().warn("Found missing mods for Blueprint, some blocks may be missing: " + requiredMods.get(i)); @@ -557,14 +557,14 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol } } - final int oldDataVersion = tag.contains("mcversion") ? tag.getInt("mcversion") : DEFAULT_FIXER_IF_NOT_FOUND; + final int oldDataVersion = tag.contains("mcversion") ? tag.getIntOr("mcversion", 0) : DEFAULT_FIXER_IF_NOT_FOUND; // Reading Pallete ListTag paletteTag = (ListTag) tag.get("palette"); List palette = fixPalette(oldDataVersion, paletteTag); // Reading Blocks - short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks"), sizeX, sizeY, sizeZ); + short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks").orElse(new int[0]), sizeX, sizeY, sizeZ); // Reading Tile Entities CompoundTag[] tileEntities = fixTileEntities(oldDataVersion, (ListTag) tag.get("tile_entities")); @@ -577,32 +577,32 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol fixCross1343(palette, blocks, tileEntities, entities); } - final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods, provider) + final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods) .setMissingMods(missingMods.toArray(new String[0])); schem.setEntities(entities); - if (tag.getAllKeys().contains("name")) + if (tag.keySet().contains("name")) { - schem.setName(tag.getString("name")); + schem.setName(tag.getStringOr("name", "")); } - if (tag.getAllKeys().contains("architects")) + if (tag.keySet().contains("architects")) { ListTag architectsTag = (ListTag) tag.get("architects"); String[] architects = new String[architectsTag.size()]; for (int i = 0; i < architectsTag.size(); i++) { - architects[i] = architectsTag.getString(i); + architects[i] = architectsTag.getString(i).orElse(""); } schem.setArchitects(architects); } - if (tag.getAllKeys().contains(NBT_OPTIONAL_DATA_TAG)) + if (tag.keySet().contains(NBT_OPTIONAL_DATA_TAG)) { - final CompoundTag optionalTag = tag.getCompound(NBT_OPTIONAL_DATA_TAG); - if (optionalTag.getAllKeys().contains(MOD_ID)) + final CompoundTag optionalTag = tag.getCompoundOrEmpty(NBT_OPTIONAL_DATA_TAG); + if (optionalTag.keySet().contains(MOD_ID)) { - final CompoundTag structurizeTag = optionalTag.getCompound(MOD_ID); + final CompoundTag structurizeTag = optionalTag.getCompoundOrEmpty(MOD_ID); BlockPos offsetPos = BlockPosUtil.readFromNBT(structurizeTag, "primary_offset"); schem.setCachePrimaryOffset(offsetPos); } @@ -640,7 +640,7 @@ public static void writeToStream(OutputStream os, Blueprint schem) * @param sizeZ Sturcture size on the Z-Axis * @return An 1 Dimensional int array */ - private static int[] convertBlocksToSaveData(short[][][] multDimArray, int sizeX, int sizeY, int sizeZ) + private static int[] convertBlocksToSaveData(short[][][] multDimArray, short sizeX, short sizeY, short sizeZ) { // Converting 3 Dimensional Array to One DImensional short[] oneDimArray = new short[sizeX * sizeY * sizeZ]; diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java index 6358aa398b..67c116bebe 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java @@ -2,24 +2,25 @@ import com.ldtteam.structurize.client.BlueprintBlockInfoTransformHandler; import com.ldtteam.structurize.client.BlueprintEntityInfoTransformHandler; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.util.BlockEntityInfo; import com.ldtteam.structurize.util.BlockInfo; +import com.ldtteam.structurize.util.EntityNbtHelper; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.model.data.ModelData; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -48,16 +49,12 @@ public static Map instantiateTileEntities(final Blueprint .filter(BlockInfo::hasTileEntityData) .map(blockInfo -> { @Nullable - final BlockEntity be = constructTileEntity(blockInfo, beLevel, blueprint.getRegistryAccess()); + final BlockEntity be = constructTileEntity(blockInfo, beLevel); if (be != null) { teModelData.put(blockInfo.getPos(), be.getModelData()); return new BlockEntityInfo(blockInfo.getPos(), be); } - else - { - Log.getLogger().error("TileEntity creation failed for: " + blueprint + " " + blockInfo.getPos()); - } return null; }) .filter(Objects::nonNull) @@ -82,11 +79,11 @@ public static List instantiateEntities(final Blueprint blueprint, final } @Nullable - public static BlockEntity constructTileEntity(final BlockInfo info, final Level beLevel, final HolderLookup.Provider provider) + public static BlockEntity constructTileEntity(final BlockInfo info, final Level beLevel) { if (info == null || info.getTileEntityData() == null) return null; - final String entityId = info.getTileEntityData().getString("id"); + final String entityId = info.getTileEntityData().getStringOr("id", ""); try { @@ -96,7 +93,13 @@ public static BlockEntity constructTileEntity(final BlockInfo info, final Level compound.putInt("z", info.getPos().getZ()); final BlockState blockState = info.getState(); - final BlockEntity entity = BlockEntity.loadStatic(info.getPos(), Objects.requireNonNull(blockState), compound, provider); + final BlockEntity entity = BlockEntity.loadStatic( + info.getPos(), + Objects.requireNonNull(blockState), + compound, + beLevel == null + ? net.minecraft.core.RegistryAccess.fromRegistryOfRegistries(net.minecraft.core.registries.BuiltInRegistries.REGISTRY) + : beLevel.registryAccess()); if (entity != null) { @@ -125,31 +128,30 @@ private static Entity constructEntity(@Nullable final CompoundTag info, final Le { if (info == null) return null; - final String entityId = info.getString("id"); + final String entityId = info.getStringOr("id", ""); try { final CompoundTag compound = info.copy(); - compound.putUUID("UUID", UUID.randomUUID()); - final Optional> type = EntityType.by(compound); - if (type.isPresent()) - { - final Entity entity = type.get().create(entityLevel); - - if (entity != null) - { - entity.load(compound); + compound.put("UUID", new net.minecraft.nbt.IntArrayTag( + net.minecraft.core.UUIDUtil.uuidToIntArray(UUID.randomUUID()))); + final Entity entity = EntityType.loadEntityRecursive( + compound, + entityLevel, + new EntitySpawnRequest(EntitySpawnReason.LOAD, false), + loaded -> loaded); - // prevent ticking rotations - entity.setOldPosAndRot(); - if (entity instanceof LivingEntity lentity) + if (entity != null) + { + // prevent ticking rotations + entity.setOldPosAndRot(); + if (entity instanceof LivingEntity lentity) { - lentity.yHeadRotO = lentity.yHeadRot; - lentity.yBodyRotO = lentity.yBodyRot; - } - - return entity; + lentity.yHeadRotO = lentity.yHeadRot; + lentity.yBodyRotO = lentity.yBodyRot; } + + return entity; } return null; } diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java index 68a9da72df..8418ccfadc 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java @@ -2,6 +2,10 @@ import com.mojang.datafixers.DSL.TypeReference; import com.mojang.serialization.Dynamic; +import com.google.common.collect.Maps; +import java.util.Map; +import net.minecraft.util.datafix.ExtraDataFixUtils; +import com.mojang.datafixers.DataFixUtils; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.SharedConstants; @@ -18,6 +22,47 @@ public class DataFixerUtils */ public static boolean isVanillaDF = DataFixers.getDataFixer() instanceof com.mojang.datafixers.DataFixerUpper; + /** + * Legacy flower-pot block-entity to block-state mappings retained for pre-1.13 blueprint migration. + */ + public static final Map> FLOWER_POT_MAP = DataFixUtils.make(Maps.newHashMap(), map -> { + map.put("minecraft:air0", ExtraDataFixUtils.blockState("minecraft:flower_pot")); + map.put("minecraft:red_flower0", ExtraDataFixUtils.blockState("minecraft:potted_poppy")); + map.put("minecraft:red_flower1", ExtraDataFixUtils.blockState("minecraft:potted_blue_orchid")); + map.put("minecraft:red_flower2", ExtraDataFixUtils.blockState("minecraft:potted_allium")); + map.put("minecraft:red_flower3", ExtraDataFixUtils.blockState("minecraft:potted_azure_bluet")); + map.put("minecraft:red_flower4", ExtraDataFixUtils.blockState("minecraft:potted_red_tulip")); + map.put("minecraft:red_flower5", ExtraDataFixUtils.blockState("minecraft:potted_orange_tulip")); + map.put("minecraft:red_flower6", ExtraDataFixUtils.blockState("minecraft:potted_white_tulip")); + map.put("minecraft:red_flower7", ExtraDataFixUtils.blockState("minecraft:potted_pink_tulip")); + map.put("minecraft:red_flower8", ExtraDataFixUtils.blockState("minecraft:potted_oxeye_daisy")); + map.put("minecraft:yellow_flower0", ExtraDataFixUtils.blockState("minecraft:potted_dandelion")); + map.put("minecraft:sapling0", ExtraDataFixUtils.blockState("minecraft:potted_oak_sapling")); + map.put("minecraft:sapling1", ExtraDataFixUtils.blockState("minecraft:potted_spruce_sapling")); + map.put("minecraft:sapling2", ExtraDataFixUtils.blockState("minecraft:potted_birch_sapling")); + map.put("minecraft:sapling3", ExtraDataFixUtils.blockState("minecraft:potted_jungle_sapling")); + map.put("minecraft:sapling4", ExtraDataFixUtils.blockState("minecraft:potted_acacia_sapling")); + map.put("minecraft:sapling5", ExtraDataFixUtils.blockState("minecraft:potted_dark_oak_sapling")); + map.put("minecraft:red_mushroom0", ExtraDataFixUtils.blockState("minecraft:potted_red_mushroom")); + map.put("minecraft:brown_mushroom0", ExtraDataFixUtils.blockState("minecraft:potted_brown_mushroom")); + map.put("minecraft:deadbush0", ExtraDataFixUtils.blockState("minecraft:potted_dead_bush")); + map.put("minecraft:tallgrass2", ExtraDataFixUtils.blockState("minecraft:potted_fern")); + map.put("minecraft:cactus0", ExtraDataFixUtils.blockState("minecraft:potted_cactus")); + }); + + /** + * Legacy note-block block-entity to block-state mappings retained for pre-1.13 blueprint migration. + */ + public static final Map> NOTE_BLOCK_MAP = DataFixUtils.make(Maps.newHashMap(), map -> { + for (int note = 0; note < 26; note++) + { + map.put("true" + note, + ExtraDataFixUtils.blockState("minecraft:note_block", Map.of("powered", "true", "note", String.valueOf(note)))); + map.put("false" + note, + ExtraDataFixUtils.blockState("minecraft:note_block", Map.of("powered", "false", "note", String.valueOf(note)))); + } + }); + /** * Private constructor to hide implicit one. */ @@ -28,12 +73,12 @@ private DataFixerUtils() public static CompoundTag runDataFixer(final CompoundTag dataIn, final TypeReference dataType, final DataVersion startVersion) { - return runDataFixer(dataIn, dataType, startVersion.getDataVersion(), SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + return runDataFixer(dataIn, dataType, startVersion.getDataVersion(), SharedConstants.getCurrentVersion().dataVersion().version()); } public static CompoundTag runDataFixer(final CompoundTag dataIn, final TypeReference dataType, final int startVersion) { - return runDataFixer(dataIn, dataType, startVersion, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + return runDataFixer(dataIn, dataType, startVersion, SharedConstants.getCurrentVersion().dataVersion().version()); } public static CompoundTag runDataFixer(final CompoundTag dataIn, final TypeReference dataType, final DataVersion startVersion, final DataVersion endVersion) diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java index 83094fc548..99467998bf 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java @@ -12,16 +12,12 @@ public enum DataVersion * - successors match * - upcoming has data version = (latest data version + 1) */ - UPCOMING(3955 + 1, null, null), + UPCOMING(4903 + 1, null, null), - v1_21_1(3955, "1.21.1", UPCOMING), - v1_21(3953, "1.21", v1_21_1), - v1_20_6(3839, "1.20.6", v1_21), - v1_20_5(3837, "1.20.5", v1_20_6), - v1_20_4(3700, "1.20.4", v1_20_5), - v1_20_3(3698, "1.20.3", v1_20_4), - v1_20_2(3578, "1.20.2", v1_20_3), - v1_20_1(3465, "1.20.1", v1_20_2), + v26_2(4903, "26.2", UPCOMING), + v1_21_1(3955, "1.21.1", v26_2), + + v1_20_1(3465, "1.20.1", v1_21_1), v1_20(3463, "1.20", v1_20_1), v1_19_4(3328, "1.19.4", v1_20), v1_19_3(3218, "1.19.3", v1_19_4), @@ -69,7 +65,7 @@ public enum DataVersion v1_9(169, "1.9", v1_9_1), DEFAULT(0, null, v1_9); - public static final DataVersion CURRENT = findFromDataVersion(SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + public static final DataVersion CURRENT = findFromDataVersion(SharedConstants.getCurrentVersion().dataVersion().version()); private final int dataVersion; private final String mcVersion; private final DataVersion successor; diff --git a/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java b/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java index be7597bc0c..174078667f 100644 --- a/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java +++ b/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java @@ -3,13 +3,13 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import net.minecraft.client.Minecraft; +import net.minecraft.util.profiling.Profiler; import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.Entity; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import java.util.Collection; +import net.neoforged.neoforge.client.event.SubmitCustomGeometryEvent; + import java.util.List; import java.util.concurrent.TimeUnit; @@ -70,18 +70,26 @@ public static BlueprintHandler getInstance() * @param pos position to render at * @param ctx rendering event */ - public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final RenderLevelStageEvent ctx) + public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final SubmitCustomGeometryEvent ctx) + { + internalBackportDraw(previewData, pos, ctx); + } + + /** + * DO NOT USE IN MCOL + */ + public void internalBackportDraw(final BlueprintPreviewData previewData, final BlockPos pos, final SubmitCustomGeometryEvent ctx) { if (previewData == null || previewData.getBlueprint() == null) { Log.getLogger().warn("Trying to draw null blueprint!"); return; } - Minecraft.getInstance().getProfiler().push("struct_render_cache"); + Profiler.get().push("struct_render_cache"); rendererCache.getUnchecked(previewData.getRenderKey()).draw(previewData, pos, ctx); - Minecraft.getInstance().getProfiler().pop(); + Profiler.get().pop(); } /** @@ -108,15 +116,15 @@ public void clearCache() * @param ctx rendering event */ public void drawAtListOfPositions(final BlueprintPreviewData previewData, - final Collection points, - final RenderLevelStageEvent ctx) + final List points, + final SubmitCustomGeometryEvent ctx) { if (points.isEmpty() || previewData == null || previewData.getBlueprint() == null) { return; } - Minecraft.getInstance().getProfiler().push("struct_render_multi"); + Profiler.get().push("struct_render_multi"); final BlueprintRenderer renderer = rendererCache.getUnchecked(previewData.getRenderKey()); @@ -125,15 +133,6 @@ public void drawAtListOfPositions(final BlueprintPreviewData previewData, renderer.draw(previewData, coord, ctx); } - Minecraft.getInstance().getProfiler().pop(); - } - - /** - * @return list of entities for instantiated renderer (potentially immediately invalid), else empty list - */ - public List getOptionalEntitiesForBlueprint(final BlueprintPreviewData previewData) - { - final BlueprintRenderer renderer = rendererCache.getIfPresent(previewData.getRenderKey()); - return renderer == null ? List.of() : renderer.entities; + Profiler.get().pop(); } } diff --git a/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java b/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java index ba4c6acb81..62a989c8e4 100644 --- a/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java +++ b/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java @@ -6,85 +6,85 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtils; import com.ldtteam.structurize.client.fakelevel.BlueprintBlockAccess; -import com.ldtteam.structurize.component.CapturedBlock; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import com.ldtteam.structurize.tag.ModTags; import com.ldtteam.structurize.util.BlockInfo; -import com.ldtteam.structurize.util.BlueprintMissHitResult; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.platform.GlStateManager.DestFactor; -import com.mojang.blaze3d.platform.GlStateManager.SourceFactor; -import com.mojang.blaze3d.platform.Lighting; -import com.mojang.blaze3d.shaders.Uniform; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; -import com.mojang.blaze3d.vertex.VertexBuffer.Usage; -import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.CrashReport; -import net.minecraft.ReportType; +import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.client.Camera; -import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.block.BlockRenderDispatcher; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.client.renderer.culling.Frustum; -import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.block.FluidRenderer; +import net.minecraft.client.renderer.block.ModelBlockRenderer; +import net.minecraft.client.renderer.block.MovingBlockRenderState; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.chunk.ChunkSectionLayer; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.rendertype.RenderTypes; +import net.minecraft.client.renderer.state.level.CameraRenderState; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.network.chat.Component; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.util.ARGB; import net.minecraft.world.entity.Entity; -import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.*; -import net.minecraft.world.level.block.entity.*; -import net.minecraft.world.level.block.entity.vault.VaultBlockEntity; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.ColorResolver; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CampfireBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SkullBlock; +import net.minecraft.world.level.block.entity.BeaconBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.CampfireBlockEntity; +import net.minecraft.world.level.block.entity.EnchantingTableBlockEntity; +import net.minecraft.world.level.block.entity.SkullBlockEntity; +import net.minecraft.world.level.block.entity.SpawnerBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import net.neoforged.neoforge.client.model.data.ModelData; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.lwjgl.opengl.GL20C; +import net.neoforged.neoforge.client.event.SubmitCustomGeometryEvent; +import net.neoforged.neoforge.model.data.ModelData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; /** - * The renderer for blueprint. - * Holds all information required to render a blueprint. + * Prepares and submits blueprint preview geometry through Minecraft's current level render pipeline. */ public class BlueprintRenderer implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintRenderer.class); - - private static final RenderBuffers renderBuffers = new RenderBuffers(0); + public static final float TRANSPARENCY_THRESHOLD = 0.99F; private static boolean hasWarnedExceptions = false; private final BlueprintBlockAccess blockAccess; - List entities = List.of(); - private List tileEntities; - private Map vertexBuffers; + private final List entities = new ArrayList<>(); + private final List tileEntities = new ArrayList<>(); + private final List blockStates = new ArrayList<>(); + private final List fluidInstances = new ArrayList<>(); private long lastGameTime; - private boolean bypassMainFrustum = false; private Set crashingObjects = Collections.newSetFromMap(new IdentityHashMap<>()); - /** - * Static factory utility method to handle the extraction of the values from the blueprint. - * - * @param blueprint The blueprint to create an instance for. - * @return The renderer. - */ public static BlueprintRenderer buildRendererForBlueprint(final Blueprint blueprint) { - final BlueprintBlockAccess blockAccess = new BlueprintBlockAccess(blueprint); - return new BlueprintRenderer(blockAccess); + return new BlueprintRenderer(new BlueprintBlockAccess(blueprint)); } private BlueprintRenderer(final BlueprintBlockAccess blockAccess) @@ -92,14 +92,10 @@ private BlueprintRenderer(final BlueprintBlockAccess blockAccess) this.blockAccess = blockAccess; } - /** - * Updates blueprint reference if it has same hash. - * - * @param previewData blueprint and context from active structure - */ public void updateBlueprint(final BlueprintPreviewData previewData) { - if (blockAccess.getLevelSource() != previewData.getBlueprint() && blockAccess.getLevelSource().hashCode() == previewData.getBlueprint().hashCode()) + if (blockAccess.getLevelSource() != previewData.getBlueprint() + && blockAccess.getLevelSource().hashCode() == previewData.getBlueprint().hashCode()) { blockAccess.setLevelSource(previewData.getBlueprint()); } @@ -107,119 +103,112 @@ public void updateBlueprint(final BlueprintPreviewData previewData) private void init(final BlueprintPreviewData previewData, final Map suppressedExceptions) { + final Minecraft minecraft = Minecraft.getInstance(); final Blueprint blueprint = previewData.getBlueprint(); - final BlockRenderDispatcher blockRenderer = Minecraft.getInstance().getBlockRenderer(); - final RandomSource random = RandomSource.create(); - + // Blueprint tile entities can contribute model data that is required by + // their block models (for example, Domum Ornamentum and MineColonies + // blocks). Keep that data keyed by the blueprint-local position just as + // the legacy baked-model renderer did. Passing ModelData.EMPTY for all + // blocks makes those models collect no render parts, which leaves only + // the placement outline visible in the preview. final Map teModelData = new HashMap<>(); - final Map tileEntitiesMap = BlueprintUtils.instantiateTileEntities(blueprint, blockAccess, teModelData); - entities = BlueprintUtils.instantiateEntities(blueprint, blockAccess); + + clearCachedState(); + + final Map tileEntitiesMap = + BlueprintUtils.instantiateTileEntities(blueprint, blockAccess, teModelData); + entities.addAll(BlueprintUtils.instantiateEntities(blueprint, blockAccess)); blockAccess.setBlockEntities(tileEntitiesMap); blockAccess.setEntities(entities); blockAccess.setSolidSubstitutionOverride(previewData.getSolidSubstitutionOverride()); blockAccess.setRenderBlocksNiceOverride(previewData.getRenderBlocksNice()); - final PoseStack matrixStack = new PoseStack(); - matrixStack.translate(0.001, 0.001, 0.001); - - final ChunkOffsetBufferBuilderWrapper fluidBufferWrapper = new ChunkOffsetBufferBuilderWrapper(); - final Map chunkBuffers = new Reference2ObjectArrayMap<>(RenderType.chunkBufferLayers().size()); - RenderType.chunkBufferLayers().forEach(type -> chunkBuffers.put(type, new BufferBuilder(renderBuffers.fixedBufferPack().buffer(type), type.mode(), type.format()))); - for (final BlockInfo blockInfo : blueprint.getBlockInfoAsList()) { final BlockPos blockPos = blockInfo.getPos(); BlockState state = blockInfo.getState(); - // specially handle blockTagSub here cuz of block entity changes - if (previewData.getRenderBlocksNice() && state.getBlock() == ModBlocks.blockTagSubstitution.get()) + + try { - if (tileEntitiesMap.remove(blockPos) instanceof final BlockEntityTagSubstitution tagTE) + if (previewData.getRenderBlocksNice() && state.getBlock() == ModBlocks.blockTagSubstitution.get()) { - final CapturedBlock replacement = tagTE.getReplacement(); - state = replacement.blockState(); - - replacement.serializedBE().map(tag -> BlockEntity.loadStatic(blockPos, replacement.blockState(), tag, blueprint.getRegistryAccess())).ifPresent(newBe -> { - newBe.setLevel(blockAccess); - teModelData.put(blockPos, newBe.getModelData()); - tileEntitiesMap.put(blockPos, newBe); - }); + if (tileEntitiesMap.remove(blockPos) instanceof final BlockEntityTagSubstitution tagTE) + { + final BlockEntityTagSubstitution.ReplacementBlock replacement = tagTE.getReplacement(); + state = replacement.getBlockState(); + + Optional.ofNullable(replacement.createBlockEntity(blockPos)).ifPresent(newBe -> { + newBe.setLevel(blockAccess); + teModelData.put(blockPos, newBe.getModelData()); + tileEntitiesMap.put(blockPos, newBe); + }); + } + else + { + state = Blocks.AIR.defaultBlockState(); + } } else { - state = Blocks.AIR.defaultBlockState(); + state = blockAccess.prepareBlockStateForRendering(state, blockPos); } - } - else - { - state = blockAccess.prepareBlockStateForRendering(state, blockPos); - } - final FluidState fluidState = state.getFluidState(); - try - { - if (!fluidState.isEmpty()) + if (state.isAir()) { - final RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState); - - final int chunkOffsetX = blockPos.getX() - (blockPos.getX() & 15), - chunkOffsetY = blockPos.getY() - (blockPos.getY() & 15), - chunkOffsetZ = blockPos.getZ() - (blockPos.getZ() & 15); + continue; + } - fluidBufferWrapper.setOffset(chunkBuffers.get(renderType), chunkOffsetX, chunkOffsetY, chunkOffsetZ); - blockRenderer.renderLiquid(blockPos, blockAccess, fluidBufferWrapper, state, fluidState); + final FluidState fluidState = state.getFluidState(); + if (!fluidState.isEmpty()) + { + fluidInstances.add(new FluidInstance(blockPos, state, fluidState)); } if (state.getRenderShape() != RenderShape.INVISIBLE) { - final BakedModel blockModel = blockRenderer.getBlockModel(state); - final ModelData modelData = blockModel.getModelData(blockAccess, blockPos, state, teModelData.getOrDefault(blockPos, ModelData.EMPTY)); - - matrixStack.pushPose(); - matrixStack.translate(blockPos.getX(), blockPos.getY(), blockPos.getZ()); - - for (final RenderType renderType : blockModel.getRenderTypes(state, random, modelData)) - { - final BufferBuilder buffer = chunkBuffers.get(renderType); - blockRenderer.renderBatched(state, blockPos, blockAccess, matrixStack, buffer, true, random, modelData, renderType); - renderType.clearRenderState(); - } - matrixStack.popPose(); + blockStates.add(createMovingBlockState( + minecraft, + blockPos, + state, + teModelData.getOrDefault(blockPos, ModelData.EMPTY))); } - } - catch (final ReportedException e) + catch (final ReportedException exception) { - suppressedExceptions.put(blockInfo, e); + suppressedExceptions.put(blockInfo, exception); } } blockAccess.setSolidSubstitutionOverride(null); blockAccess.setRenderBlocksNiceOverride(Structurize.getConfig().getClient().renderPlaceholdersNice.get()); + tileEntities.addAll(tileEntitiesMap.values()); + } - clearVertexBuffers(); - vertexBuffers = new Reference2ObjectArrayMap<>(RenderType.chunkBufferLayers().size()); - chunkBuffers.forEach((type, buffer) -> { - final MeshData meshData = buffer.build(); - if (meshData != null) - { - final VertexBuffer vertexBuffer = new VertexBuffer(Usage.STATIC); - vertexBuffer.bind(); - vertexBuffer.upload(meshData); - vertexBuffers.put(type, vertexBuffer); - } - }); - VertexBuffer.unbind(); - - tileEntities = new ArrayList<>(tileEntitiesMap.values()); + private MovingBlockRenderState createMovingBlockState( + final Minecraft minecraft, + final BlockPos pos, + final BlockState state, + final ModelData modelData) + { + final MovingBlockRenderState renderState = new MovingBlockRenderState(); + renderState.randomSeedPos = pos; + renderState.blockPos = pos; + renderState.blockState = state; + renderState.modelData = modelData; + renderState.cardinalLighting = minecraft.level.cardinalLighting(); + renderState.lightEngine = blockAccess.getLightEngine(); + + final ClientLevel realLevel = minecraft.level; + if (realLevel != null) + { + renderState.biome = realLevel.getBiome(blockAccess.getWorldPos().offset(pos)); + } + return renderState; } - /** - * Draws structure into world. - */ - public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final RenderLevelStageEvent ctx) + public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final SubmitCustomGeometryEvent ctx) { - // we've crashed hard before, full skip if (crashingObjects == null) { return; @@ -227,443 +216,472 @@ public void draw(final BlueprintPreviewData previewData, final BlockPos pos, fin try { - final Map suppressedExceptions = drawUnsafe(previewData, pos, ctx); - if (!suppressedExceptions.isEmpty()) - { - if (!hasWarnedExceptions) - { - hasWarnedExceptions = true; - Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.preview_renderer.exception")); - } - - boolean crashReported = false; - boolean isEmpty = true; - for (final Map.Entry e : suppressedExceptions.entrySet()) - { - if (!crashingObjects.add(e.getKey())) - { - continue; - } - isEmpty = false; - - if (e.getValue() instanceof final ReportedException reportedException) - { - printCrashReport(reportedException.getReport(), previewData); - crashReported = true; - } - else - { - LOGGER.error("", e.getValue()); - } - } - - if (!crashReported && !isEmpty) - { - printCrashReport(CrashReport.forThrowable(new Exception(), "Small exception, rendering partially"), previewData); - } - } + reportSuppressedExceptions(previewData, drawUnsafe(previewData, pos, ctx)); } - catch (final Exception e) + catch (final Exception exception) { - printCrashReport(CrashReport.forThrowable(e, "Fatal exception, cannot render"), previewData); + final CrashReport crashReport = CrashReport.forThrowable(exception, "Rendering blueprint"); + final CrashReportCategory category = crashReport.addCategory("Blueprint:"); + previewData.getBlueprint().describeSelfInCrashReport(category); + LOGGER.error(crashReport.getDetails()); crashingObjects = null; - Minecraft.getInstance().player.sendSystemMessage( - Component.translatable("structurize.preview_renderer.cannot_render", previewData.getBlueprint().getName())); + final var player = Minecraft.getInstance().player; + if (player != null) + { + player.sendSystemMessage(Component.translatable( + "structurize.preview_renderer.cannot_render", previewData.getBlueprint().getName())); + } } } - private static void printCrashReport(final CrashReport report, final BlueprintPreviewData previewData) - { - previewData.getBlueprint().describeSelfInCrashReport(report.addCategory("Blueprint")); - LOGGER.error(report.getFriendlyReport(new ReportType("Problem during blueprint rendering", ReportType.TEST.nuggets()))); - } - - /** - * Draws structure into world. - * - * @return suppressed exceptions - */ - public Map drawUnsafe(final BlueprintPreviewData previewData, final BlockPos pos, final RenderLevelStageEvent ctx) + private void reportSuppressedExceptions( + final BlueprintPreviewData previewData, + final Map suppressedExceptions) { - final BlockPos anchorPos = pos.subtract(previewData.getBlueprint().getPrimaryBlockOffset()); - - // cull entire rendering - if (!ctx.getFrustum().isVisible(previewData.getBlueprint().getAABB().move(anchorPos)) && !bypassMainFrustum) + if (suppressedExceptions.isEmpty()) { - return Map.of(); + return; } - - final Map suppressedExceptions = new IdentityHashMap<>(); - final Minecraft mc = Minecraft.getInstance(); - final long gameTime = mc.level.getGameTime(); - final PoseStack matrixStack = ctx.getPoseStack(); - final DeltaTracker deltaTracker = ctx.getPartialTick(); - final ProfilerFiller profiler = mc.getProfiler(); - final Matrix4f mvMatrix = ctx.getModelViewMatrix(); - final Matrix4f pMatrix = ctx.getProjectionMatrix(); - - profiler.push("struct_render_init"); - - // make sure instances are synced - updateBlueprint(previewData); - blockAccess.setWorldPos(anchorPos); - // init - if (vertexBuffers == null) + if (!hasWarnedExceptions) { - init(previewData, suppressedExceptions); + hasWarnedExceptions = true; + final var player = Minecraft.getInstance().player; + if (player != null) + { + player.sendSystemMessage(Component.translatable("structurize.preview_renderer.exception")); + } } - profiler.popPush("struct_render_prepare"); - final Vec3 viewPosition = ctx.getCamera().getPosition(); - final Vec3 realRenderRootVecd = Vec3.atLowerCornerOf(anchorPos).subtract(viewPosition); - final Vector3f realRenderRootVecf = realRenderRootVecd.toVector3f(); - - final float partialTicks; + boolean crashReported = false; + boolean isEmpty = true; + for (final Map.Entry entry : suppressedExceptions.entrySet()) { - final Entity entity = mc.getCameraEntity() == null ? mc.player : mc.getCameraEntity(); - partialTicks = mc.level.tickRateManager().isEntityFrozen(entity) ? 1.0F : deltaTracker.getGameTimeDeltaPartialTick(!mc.level.tickRateManager().isFrozen()); + if (!crashingObjects.add(entry.getKey())) + { + continue; + } + isEmpty = false; + + if (entry.getValue() instanceof final ReportedException reportedException) + { + previewData.getBlueprint() + .describeSelfInCrashReport(reportedException.getReport().addCategory("Rendering blueprint")); + LOGGER.error(reportedException.getReport().getDetails()); + crashReported = true; + } + else + { + LOGGER.error("", entry.getValue()); + } } - // cache old dispatchers - final Level dispLevel = mc.getBlockEntityRenderDispatcher().level; // they are same for both anyway - final Camera dispCamera = mc.getBlockEntityRenderDispatcher().camera; // also same - final HitResult beHitResult = mc.getBlockEntityRenderDispatcher().cameraHitResult; - final Entity ePickEntity = mc.getEntityRenderDispatcher().crosshairPickEntity; + if (!crashReported && !isEmpty) + { + final CrashReport crashReport = CrashReport.forThrowable(new Exception(), "Summary"); + previewData.getBlueprint().describeSelfInCrashReport(crashReport.addCategory("Rendering blueprint")); + LOGGER.error(crashReport.getDetails()); + } + } - final Camera ourCamera = new Camera(); - ourCamera.setup(blockAccess, - dispCamera.getEntity(), - !mc.options.getCameraType().isFirstPerson(), - mc.options.getCameraType().isMirrored(), - partialTicks); - ourCamera.setPosition(viewPosition.subtract(anchorPos.getX(), anchorPos.getY(), anchorPos.getZ())); + public Map drawUnsafe( + final BlueprintPreviewData previewData, + final BlockPos pos, + final SubmitCustomGeometryEvent ctx) + { + updateBlueprint(previewData); + final BlockPos anchorPos = pos.subtract(previewData.getBlueprint().getPrimaryBlockOffset()); + blockAccess.setWorldPos(anchorPos); - mc.getBlockEntityRenderDispatcher().prepare(blockAccess, ourCamera, BlueprintMissHitResult.MISS); - mc.getEntityRenderDispatcher().prepare(blockAccess, ourCamera, mc.crosshairPickEntity); + if (blockStates.isEmpty() && fluidInstances.isEmpty() && entities.isEmpty() && tileEntities.isEmpty()) + { + init(previewData, new IdentityHashMap<>()); + } - final Frustum blueprintLocalFrustum = new Frustum(ctx.getFrustum()); - blueprintLocalFrustum.prepare(ourCamera.getPosition().x(), ourCamera.getPosition().y(), ourCamera.getPosition().z()); - bypassMainFrustum = false; + final Map suppressedExceptions = new IdentityHashMap<>(); + final Minecraft minecraft = Minecraft.getInstance(); + final long gameTime = minecraft.level.getGameTime(); + final float partialTicks = minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(false); + final Vec3 viewPosition = minecraft.gameRenderer.mainCamera().position(); + final Vec3 realRoot = Vec3.atLowerCornerOf(anchorPos).subtract(viewPosition); + + final PoseStack poseStack = ctx.getPoseStack(); + poseStack.pushPose(); + poseStack.translate(realRoot.x(), realRoot.y(), realRoot.z()); + + // This collector is owned by LevelRenderer and is consumed by the + // current frame's feature dispatcher. A private SubmitNodeStorage is + // never rendered and makes the preview silently disappear. + final SubmitNodeCollector collector = ctx.getSubmitNodeCollector(); + submitBlocks(minecraft, collector, poseStack, previewData); + submitFluids(minecraft, collector, poseStack); + submitEntities(minecraft, collector, poseStack, gameTime, partialTicks, suppressedExceptions); + submitBlockEntities(minecraft, collector, poseStack, anchorPos, gameTime, partialTicks); + + poseStack.popPose(); + lastGameTime = gameTime; + return suppressedExceptions; + } - // missing chunk system! else done? + private void submitBlocks( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack, + final BlueprintPreviewData previewData) + { + final float previewAlpha = previewAlpha(previewData); + final boolean blendPreview = previewAlpha >= 0.0F && previewAlpha < TRANSPARENCY_THRESHOLD; + final ModelBlockRenderer translucentRenderer = blendPreview + ? new ModelBlockRenderer(minecraft.options.ambientOcclusion().get(), false, minecraft.getBlockColors()) + : null; - if (mc.level.effects().constantAmbientLight()) + for (final MovingBlockRenderState state : blockStates) { - Lighting.setupNetherLevel(); + // MovingBlockFeatureRenderer tessellates a block at the origin of + // the submitted pose. The render state keeps the local position for + // lighting/model-data lookups, but it is not used as a translation. + // Apply the blueprint-local offset here or every block collapses at + // the anchor (and is effectively hidden by the terrain). + final BlockPos blockPos = state.blockPos; + poseStack.pushPose(); + // Retain the small legacy offset to avoid z-fighting with the + // terrain when a preview is placed directly on existing blocks. + poseStack.translate(blockPos.getX() + 0.01F, blockPos.getY() + 0.01F, blockPos.getZ() + 0.01F); + if (!blendPreview) + { + collector.submitMovingBlock(poseStack, state, 0); + } + else + { + final BlockStateModel model = minecraft.getModelManager().getBlockStateModelSet().get(state.blockState); + collector.submitCustomGeometry( + poseStack, + RenderTypes.translucentMovingBlock(), + (pose, buffer) -> translucentRenderer.tesselateBlock( + (x, y, z, quad, instance) -> { + instance.multiplyColor(ARGB.color(previewAlpha, -1)); + final PoseStack.Pose translatedPose = pose.copy(); + translatedPose.translate(x, y, z); + buffer.putBakedQuad(translatedPose, quad, instance); + }, + 0.0F, + 0.0F, + 0.0F, + state, + state.blockPos, + state.blockState, + model, + state.blockState.getSeed(state.randomSeedPos))); + } + poseStack.popPose(); } - else + } + + private float previewAlpha(final BlueprintPreviewData previewData) + { + final float override = previewData.getOverridePreviewTransparency(); + if (override >= 0.0F) { - Lighting.setupLevel(); + return override; } + return Structurize.getConfig().getClient().rendererTransparency.get().floatValue(); + } - // Render blocks - - if (ctx.getStage() == RenderLevelStageEvent.Stage.AFTER_LEVEL) + private void submitFluids( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack) + { + if (fluidInstances.isEmpty()) { - FogRenderer.setupFog(ctx.getCamera(), - FogRenderer.FogMode.FOG_TERRAIN, - Math.max(mc.gameRenderer.getRenderDistance(), 32.0F), - mc.level.effects().isFoggyAt(Mth.floor(viewPosition.x()), Mth.floor(viewPosition.y())) - || mc.gui.getBossOverlay().shouldCreateWorldFog(), - partialTicks); + return; } - profiler.popPush("struct_render_blocks"); - renderBlockLayer(RenderType.solid(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - // FORGE: fix flickering leaves when mods mess up the blurMipmap settings - mc.getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).setBlurMipmap(false, mc.options.mipmapLevels().get() > 0); - renderBlockLayer(RenderType.cutoutMipped(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - mc.getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).restoreLastBlurMipmap(); - renderBlockLayer(RenderType.cutout(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - - profiler.popPush("struct_render_entities"); - final MultiBufferSource.BufferSource renderBufferSource = renderBuffers.bufferSource(); + final FluidRenderer fluidRenderer = new FluidRenderer(minecraft.getModelManager().getFluidStateModelSet()); + for (final FluidInstance instance : fluidInstances) + { + final ChunkSectionLayer sectionLayer = minecraft.getModelManager().getFluidStateModelSet() + .get(instance.fluidState()).layer(); + final RenderType renderType = movingRenderType(sectionLayer); + + final BlockAndTintGetter fluidLevel = new BlueprintBlockTintGetter(); + // FluidRenderer emits section-local coordinates (the same contract + // used by the old chunk-buffer wrapper). Translate the pose by the + // section origin so fluids keep their blueprint-local position. + final BlockPos fluidPos = instance.pos(); + final int sectionX = fluidPos.getX() - (fluidPos.getX() & 15); + final int sectionY = fluidPos.getY() - (fluidPos.getY() & 15); + final int sectionZ = fluidPos.getZ() - (fluidPos.getZ() & 15); + poseStack.pushPose(); + poseStack.translate(sectionX, sectionY, sectionZ); + collector.submitCustomGeometry(poseStack, renderType, (pose, buffer) -> fluidRenderer.tesselate( + fluidLevel, + fluidPos, + layer -> layer == sectionLayer ? new PoseVertexConsumer(pose, buffer) : null, + instance.state(), + instance.fluidState())); + poseStack.popPose(); + } + } - // Entities + private RenderType movingRenderType(final ChunkSectionLayer layer) + { + return switch (layer) + { + case SOLID -> RenderTypes.solidMovingBlock(); + case CUTOUT -> RenderTypes.cutoutMovingBlock(); + case TRANSLUCENT -> RenderTypes.translucentMovingBlock(); + }; + } - matrixStack.pushPose(); - matrixStack.translate(realRenderRootVecd.x(), realRenderRootVecd.y(), realRenderRootVecd.z()); + private void submitEntities( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack, + final long gameTime, + final float partialTicks, + final Map suppressedExceptions) + { + final EntityRenderDispatcher dispatcher = minecraft.getEntityRenderDispatcher(); for (final Entity entity : entities) { - if (!mc.getEntityRenderDispatcher() - .shouldRender(entity, - blueprintLocalFrustum, - ourCamera.getPosition().x(), - ourCamera.getPosition().y(), - ourCamera.getPosition().z())) - { - continue; - } - - if (gameTime != lastGameTime && entity.getType().is(ModTags.PREVIEW_TICKING_ENTITIES)) + if (gameTime != lastGameTime && entity.getType().builtInRegistryHolder().is(ModTags.PREVIEW_TICKING_ENTITIES)) { try { entity.tick(); } - catch (final Exception e) + catch (final Exception exception) { - // well, noop - suppressedExceptions.put(entity, e); + suppressedExceptions.put(entity, exception); } } - bypassMainFrustum |= entity.noCulling; try { - mc.getEntityRenderDispatcher().render(entity, - entity.getX(), - entity.getY(), - entity.getZ(), - entity.getYRot(), - partialTicks, - matrixStack, - renderBufferSource, - mc.getEntityRenderDispatcher().getPackedLightCoords(entity, partialTicks)); + final EntityRenderState state = dispatcher.extractEntity(entity, partialTicks); + dispatcher.submit(state, cameraState(minecraft), entity.getX(), entity.getY(), entity.getZ(), poseStack, collector); } - catch (final ClassCastException e) + catch (final ClassCastException | ReportedException exception) { - // Oops - suppressedExceptions.put(entity, e); + suppressedExceptions.put(entity, exception); } } - matrixStack.popPose(); - - profiler.popPush("struct_render_entities_finish"); - renderBufferSource.endLastBatch(); - renderBufferSource.endBatch(RenderType.entitySolid(InventoryMenu.BLOCK_ATLAS)); - renderBufferSource.endBatch(RenderType.entityCutout(InventoryMenu.BLOCK_ATLAS)); - renderBufferSource.endBatch(RenderType.entityCutoutNoCull(InventoryMenu.BLOCK_ATLAS)); - renderBufferSource.endBatch(RenderType.entitySmoothCutout(InventoryMenu.BLOCK_ATLAS)); - - // Block entities + } - profiler.popPush("struct_render_blockentities"); + private void submitBlockEntities( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack, + final BlockPos anchorPos, + final long gameTime, + final float partialTicks) + { + final BlockEntityRenderDispatcher dispatcher = minecraft.getBlockEntityRenderDispatcher(); + dispatcher.prepare(Vec3.ZERO); for (final BlockEntity tileEntity : tileEntities) { - final BlockEntityRenderer renderer = mc.getBlockEntityRenderDispatcher().getRenderer(tileEntity); - if (renderer == null || !renderer.shouldRender(tileEntity, ourCamera.getPosition())) + tickPreviewBlockEntity(minecraft, anchorPos, tileEntity, gameTime); + final BlockEntityRenderState state = dispatcher.tryExtractRenderState(tileEntity, partialTicks, null, false); + if (state == null) { continue; } final BlockPos tePos = tileEntity.getBlockPos(); - final Vec3 realRenderTePos = realRenderRootVecd.add(tePos.getX(), tePos.getY(), tePos.getZ()); - - if (gameTime != lastGameTime) - { - // hooks from EntityBlock#getTicker(Level, BlockState, BlockEntityType) for client side - // either mc.level and anchorPos - particles, player distance etc. - // or blockAccess and tePos - blueprint neighborhood - if (tileEntity instanceof final SpawnerBlockEntity spawner) - { - SpawnerBlockEntity.clientTick(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos), spawner); - } - else if (tileEntity instanceof final EnchantingTableBlockEntity enchTable) - { - EnchantingTableBlockEntity - .bookAnimationTick(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos), enchTable); - } - else if (tileEntity instanceof final CampfireBlockEntity campfire) - { - final BlockState bs = blockAccess.getBlockState(tePos); - if (bs.getBlock() instanceof CampfireBlock && bs.getValue(CampfireBlock.LIT)) - { - CampfireBlockEntity.particleTick(mc.level, anchorPos.offset(tePos), bs, campfire); - } - } - else if (tileEntity instanceof final SkullBlockEntity skull) - { - final BlockState bs = blockAccess.getBlockState(tePos); - if (bs.getBlock() instanceof SkullBlock && (bs.is(Blocks.DRAGON_HEAD) || bs.is(Blocks.DRAGON_WALL_HEAD) || - bs.is(Blocks.PIGLIN_HEAD) || - bs.is(Blocks.PIGLIN_WALL_HEAD))) - { - SkullBlockEntity.animation(blockAccess, tePos, bs, skull); - } - } - else if (tileEntity instanceof final BeaconBlockEntity beacon) - { - // uses sound and applies buffs, but we dont want any of this since we're preview - BeaconBlockEntity.tick(blockAccess, tePos, blockAccess.getBlockState(tePos), beacon); - } - else if (tileEntity instanceof final VaultBlockEntity vault) - { - VaultBlockEntity.Client.tick(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos), vault.getClientData(), vault.getSharedData()); - } - else if (tileEntity instanceof final TrialSpawnerBlockEntity trialSpawner) - { - trialSpawner.getTrialSpawner().tickClient(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos).getOptionalValue(TrialSpawnerBlock.OMINOUS).orElse(false)); - } - } - - bypassMainFrustum |= renderer.shouldRenderOffScreen(tileEntity); - if (!blueprintLocalFrustum.isVisible(renderer.getRenderBoundingBox(tileEntity)) && !renderer.shouldRenderOffScreen(tileEntity)) - { - continue; - } - - matrixStack.pushPose(); - matrixStack.translate(realRenderTePos.x, realRenderTePos.y, realRenderTePos.z); - - mc.getBlockEntityRenderDispatcher().render(tileEntity, partialTicks, matrixStack, renderBufferSource); - matrixStack.popPose(); + poseStack.pushPose(); + poseStack.translate(tePos.getX(), tePos.getY(), tePos.getZ()); + dispatcher.submit(state, poseStack, collector, cameraState(minecraft)); + poseStack.popPose(); } + } - profiler.popPush("struct_render_blockentities_finish"); - renderBufferSource.endBatch(RenderType.solid()); - renderBufferSource.endBatch(RenderType.endPortal()); - renderBufferSource.endBatch(RenderType.endGateway()); - renderBufferSource.endBatch(Sheets.solidBlockSheet()); - renderBufferSource.endBatch(Sheets.cutoutBlockSheet()); - renderBufferSource.endBatch(Sheets.bedSheet()); - renderBufferSource.endBatch(Sheets.shulkerBoxSheet()); - renderBufferSource.endBatch(Sheets.signSheet()); - renderBufferSource.endBatch(Sheets.hangingSignSheet()); - renderBufferSource.endBatch(Sheets.chestSheet()); - renderBuffers.outlineBufferSource().endOutlineBatch(); // not used now - - renderBufferSource.endLastBatch(); - renderBufferSource.endBatch(Sheets.translucentCullBlockSheet()); - renderBufferSource.endBatch(Sheets.bannerSheet()); - renderBufferSource.endBatch(Sheets.shieldSheet()); - renderBufferSource.endBatch(RenderType.armorEntityGlint()); - renderBufferSource.endBatch(RenderType.glint()); - renderBufferSource.endBatch(RenderType.glintTranslucent()); - renderBufferSource.endBatch(RenderType.entityGlint()); - renderBufferSource.endBatch(RenderType.entityGlintDirect()); - renderBufferSource.endBatch(RenderType.waterMask()); - renderBuffers.crumblingBufferSource().endBatch(); // not used now - - profiler.popPush("struct_render_blocks2"); - renderBlockLayer(RenderType.translucent(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - - renderBufferSource.endBatch(RenderType.lines()); - renderBufferSource.endBatch(); - renderBlockLayer(RenderType.tripwire(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); + private CameraRenderState cameraState(final Minecraft minecraft) + { + final Camera camera = minecraft.gameRenderer.mainCamera(); + final CameraRenderState state = new CameraRenderState(); + state.initialized = true; + state.pos = camera.position(); + state.blockPos = camera.blockPosition(); + state.xRot = camera.xRot(); + state.yRot = camera.yRot(); + state.orientation.set(camera.rotation()); + return state; + } - RenderSystem.applyModelViewMatrix(); // ensure no polution - Lighting.setupLevel(); - if (ctx.getStage() == RenderLevelStageEvent.Stage.AFTER_LEVEL) + private void tickPreviewBlockEntity( + final Minecraft minecraft, + final BlockPos anchorPos, + final BlockEntity tileEntity, + final long gameTime) + { + if (gameTime == lastGameTime) { - FogRenderer.setupNoFog(); + return; } - // restore vanilla setup - mc.getBlockEntityRenderDispatcher().prepare(dispLevel, dispCamera, beHitResult); - mc.getEntityRenderDispatcher().prepare(dispLevel, dispCamera, ePickEntity); - - lastGameTime = gameTime; - profiler.pop(); - - return suppressedExceptions; + final BlockPos tePos = tileEntity.getBlockPos(); + final BlockState blockState = blockAccess.getBlockState(tePos); + if (tileEntity instanceof final SpawnerBlockEntity spawner) + { + SpawnerBlockEntity.clientTick(minecraft.level, anchorPos.offset(tePos), blockState, spawner); + } + else if (tileEntity instanceof final EnchantingTableBlockEntity enchantingTable) + { + EnchantingTableBlockEntity.bookAnimationTick(minecraft.level, anchorPos.offset(tePos), blockState, enchantingTable); + } + else if (tileEntity instanceof final CampfireBlockEntity campfire + && blockState.getBlock() instanceof CampfireBlock + && blockState.getValue(CampfireBlock.LIT)) + { + CampfireBlockEntity.particleTick(minecraft.level, anchorPos.offset(tePos), blockState, campfire); + } + else if (tileEntity instanceof final SkullBlockEntity skull + && blockState.getBlock() instanceof SkullBlock + && (blockState.is(Blocks.DRAGON_HEAD) || blockState.is(Blocks.DRAGON_WALL_HEAD))) + { + SkullBlockEntity.animation(blockAccess, tePos, blockState, skull); + } + else if (tileEntity instanceof final BeaconBlockEntity beacon) + { + BeaconBlockEntity.tick(blockAccess, tePos, blockState, beacon); + } } - /** - * Clears GL references and frees GL objects. - */ - private void clearVertexBuffers() + private void clearCachedState() { - if (vertexBuffers != null) - { - vertexBuffers.values().forEach(VertexBuffer::close); - vertexBuffers = null; - } + entities.clear(); + tileEntities.clear(); + blockStates.clear(); + fluidInstances.clear(); } @Override public void close() { - clearVertexBuffers(); + clearCachedState(); + } + + private record FluidInstance(BlockPos pos, BlockState state, FluidState fluidState) + { } - private void renderBlockLayer(final RenderType layerRenderType, final Matrix4f mvMatrix, final Matrix4f pMatrix, final Vector3f realRenderRootPos, final BlueprintPreviewData previewData, final Minecraft mc) + private record PoseVertexConsumer(PoseStack.Pose pose, VertexConsumer delegate) implements VertexConsumer { - final VertexBuffer vertexBuffer = vertexBuffers.get(layerRenderType); - if (vertexBuffer == null) + @Override + public VertexConsumer addVertex(final float x, final float y, final float z) { - return; + delegate.addVertex(pose.pose(), x, y, z); + return this; } - layerRenderType.setupRenderState(); - - final ShaderInstance shaderinstance = RenderSystem.getShader(); - shaderinstance.setDefaultUniforms(VertexFormat.Mode.QUADS, mvMatrix, pMatrix, mc.getWindow()); - shaderinstance.apply(); - - final Uniform uniform = shaderinstance.CHUNK_OFFSET; - if (uniform != null) + @Override + public VertexConsumer setColor(final int red, final int green, final int blue, final int alpha) { - uniform.set(realRenderRootPos); - uniform.upload(); + delegate.setColor(red, green, blue, alpha); + return this; } - TransparencyHack.apply(previewData.getOverridePreviewTransparency()); + @Override + public VertexConsumer setColor(final int color) + { + delegate.setColor(color); + return this; + } - vertexBuffer.bind(); - vertexBuffer.draw(); + @Override + public VertexConsumer setUv(final float u, final float v) + { + delegate.setUv(u, v); + return this; + } - TransparencyHack.reset(); + @Override + public VertexConsumer setUv1(final int u, final int v) + { + delegate.setUv1(u, v); + return this; + } - if (uniform != null) + @Override + public VertexConsumer setUv2(final int u, final int v) { - uniform.set(0f, 0, 0); + delegate.setUv2(u, v); + return this; } - shaderinstance.clear(); + @Override + public VertexConsumer setNormal(final float x, final float y, final float z) + { + delegate.setNormal(x, y, z); + return this; + } - VertexBuffer.unbind(); - layerRenderType.clearRenderState(); + @Override + public VertexConsumer setLineWidth(final float width) + { + delegate.setLineWidth(width); + return this; + } } - /** - * Assuming there's no blend function active let's take advantage of OpenGL blend color constant - * which doesnt require any shader changes at all. - * More info at: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glBlendColor.xhtml - */ - public static class TransparencyHack + private final class BlueprintBlockTintGetter implements BlockAndTintGetter { - public static final float THRESHOLD = 0.99f; - protected static boolean applied = false; + @Override + public net.minecraft.world.level.CardinalLighting cardinalLighting() + { + return Minecraft.getInstance().level.cardinalLighting(); + } - public static void apply(final float overrideValue) + @Override + public net.minecraft.world.level.lighting.LevelLightEngine getLightEngine() { - if (applied || GlStateManager.BLEND.mode.enabled) - { - // do not override if there is running blend fnc - return; - } + return blockAccess.getLightEngine(); + } - float alpha = Structurize.getConfig().getClient().rendererTransparency.get().floatValue(); - if (overrideValue != -1) - { - alpha = Mth.clamp(overrideValue, 0, 1); - } + @Override + public int getBlockTint(final BlockPos pos, final ColorResolver color) + { + final ClientLevel level = Minecraft.getInstance().level; + return level == null ? -1 : color.getColor( + level.getBiome(blockAccess.getWorldPos().offset(pos)).value(), pos.getX(), pos.getZ()); + } - if (alpha < 0 || alpha > THRESHOLD) - { - return; - } + @Override + public BlockEntity getBlockEntity(final BlockPos pos) + { + return blockAccess.getBlockEntity(pos); + } - applied = true; + @Override + public BlockState getBlockState(final BlockPos pos) + { + return blockAccess.getBlockState(pos); + } - RenderSystem.enableBlend(); - RenderSystem.blendFunc(SourceFactor.CONSTANT_ALPHA, DestFactor.ONE_MINUS_CONSTANT_ALPHA); - GL20C.glBlendColor(0, 0, 0, alpha); + @Override + public FluidState getFluidState(final BlockPos pos) + { + return blockAccess.getFluidState(pos); } - public static void reset() + @Override + public int getHeight() { - if (!applied) - { - return; - } + return blockAccess.getHeight(); + } - applied = false; + @Override + public int getMinY() + { + return blockAccess.getMinY(); + } - RenderSystem.disableBlend(); + @Override + public ModelData getModelData(final BlockPos pos) + { + return ModelData.EMPTY; } } } diff --git a/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java b/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java index fd80445764..05ed307128 100644 --- a/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java +++ b/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java @@ -1,93 +1,108 @@ package com.ldtteam.structurize.client; -import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.VertexConsumer; +import org.joml.Matrix4fc; +import org.joml.Vector3f; +import org.joml.Vector3fc; + /** - * Delegating offseted bufferBuilder, delegated method @Overriden in BufferBuilder class to provide fast delegation + * Routes chunk-local vertices into the blueprint's world-space buffer. */ -public class ChunkOffsetBufferBuilderWrapper implements VertexConsumer +public final class ChunkOffsetBufferBuilderWrapper implements VertexConsumer { - private BufferBuilder delegate; + private static final ChunkOffsetBufferBuilderWrapper INSTANCE = new ChunkOffsetBufferBuilderWrapper(); + + private VertexConsumer delegate; private int offsetX; private int offsetY; private int offsetZ; - public void setOffset(final BufferBuilder delegate, final int offsetX, final int offsetY, final int offsetZ) + private ChunkOffsetBufferBuilderWrapper() + { + this.delegate = VertexConsumer.class.cast(null); + } + + public static ChunkOffsetBufferBuilderWrapper setupGlobalInstance( + final VertexConsumer delegate, + final int offsetX, + final int offsetY, + final int offsetZ) { - this.delegate = delegate; - this.offsetX = offsetX; - this.offsetY = offsetY; - this.offsetZ = offsetZ; + INSTANCE.delegate = delegate; + INSTANCE.offsetX = offsetX; + INSTANCE.offsetY = offsetY; + INSTANCE.offsetZ = offsetZ; + return INSTANCE; } @Override - public VertexConsumer addVertex(float x, float y, float z) + public VertexConsumer addVertex(final float x, final float y, final float z) { - return delegate.addVertex(offsetX + x, offsetY + y, offsetZ + z); + delegate.addVertex(offsetX + x, offsetY + y, offsetZ + z); + return this; } @Override - public VertexConsumer setColor(int p_350581_, int p_350952_, int p_350275_, int p_350985_) + public VertexConsumer addVertex(final Matrix4fc pose, final float x, final float y, final float z) { - return delegate.setColor(p_350581_, p_350952_, p_350275_, p_350985_); + final Vector3f position = pose.transformPosition(offsetX + x, offsetY + y, offsetZ + z, new Vector3f()); + delegate.addVertex(position.x(), position.y(), position.z()); + return this; } @Override - public VertexConsumer setNormal(float p_351000_, float p_350982_, float p_350974_) + public VertexConsumer addVertex(final Vector3fc position) { - return delegate.setNormal(p_351000_, p_350982_, p_350974_); + return addVertex(position.x(), position.y(), position.z()); } @Override - public VertexConsumer setUv(float p_350574_, float p_350773_) + public VertexConsumer setColor(final int red, final int green, final int blue, final int alpha) { - return delegate.setUv(p_350574_, p_350773_); + delegate.setColor(red, green, blue, alpha); + return this; } @Override - public VertexConsumer setUv1(int p_350396_, int p_350722_) + public VertexConsumer setColor(final int color) { - return delegate.setUv1(p_350396_, p_350722_); + delegate.setColor(color); + return this; } @Override - public VertexConsumer setUv2(int p_351058_, int p_350320_) + public VertexConsumer setUv(final float u, final float v) { - return delegate.setUv2(p_351058_, p_350320_); + delegate.setUv(u, v); + return this; } @Override - public VertexConsumer setColor(int p_350530_) + public VertexConsumer setUv1(final int u, final int v) { - return delegate.setColor(p_350530_); + delegate.setUv1(u, v); + return this; } @Override - public VertexConsumer setOverlay(int p_350297_) + public VertexConsumer setUv2(final int u, final int v) { - return delegate.setOverlay(p_350297_); + delegate.setUv2(u, v); + return this; } @Override - public VertexConsumer setLight(int p_350848_) + public VertexConsumer setNormal(final float x, final float y, final float z) { - return delegate.setLight(p_350848_); + delegate.setNormal(x, y, z); + return this; } @Override - public void addVertex(float x, - float y, - float z, - int p_350371_, - float p_350977_, - float p_350674_, - int p_350816_, - int p_350690_, - float p_350640_, - float p_350490_, - float p_350810_) + public VertexConsumer setLineWidth(final float width) { - delegate.addVertex(offsetX + x, offsetY + y, offsetZ + z, p_350371_, p_350977_, p_350674_, p_350816_, p_350690_, p_350640_, p_350490_, p_350810_); + delegate.setLineWidth(width); + return this; } } diff --git a/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java b/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java index c34c3b23ce..33661a8b5f 100644 --- a/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java +++ b/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java @@ -1,15 +1,13 @@ package com.ldtteam.structurize.client; -import com.ldtteam.structurize.items.ItemStackTooltip; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; -import org.jetbrains.annotations.NotNull; -import org.joml.Matrix4f; +import com.ldtteam.structurize.items.ItemStackTooltip; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; +import org.jetbrains.annotations.NotNull; public class ClientItemStackTooltip implements ClientTooltipComponent { @@ -21,9 +19,9 @@ public ClientItemStackTooltip(@NotNull final ItemStackTooltip component) } @Override - public int getHeight() - { - return 20; + public int getHeight(final Font font) + { + return 20; } @Override @@ -33,18 +31,24 @@ public int getWidth(@NotNull Font font) } @Override - public void renderText(@NotNull Font font, final int x, final int y, - @NotNull final Matrix4f pose, - @NotNull final MultiBufferSource.BufferSource buffers) - { - font.drawInBatch(this.component.getStack().getHoverName(), x + 20, y + (20 - font.lineHeight) / 2f, 0xffffffff, false, pose, buffers, Font.DisplayMode.NORMAL, 0, 0x00f000f0); - } - - @Override - public void renderImage(final Font font, final int x, final int y, final GuiGraphics target) - { - target.renderItem(this.component.getStack(), x + 2, y + 2); - target.renderItemDecorations(getFont(this.component.getStack()), this.component.getStack(), x + 2, y + 2); + public void extractText(@NotNull final GuiGraphicsExtractor graphics, + @NotNull final Font font, + final int x, + final int y) + { + graphics.text(font, this.component.getStack().getHoverName(), x + 20, y + (20 - font.lineHeight) / 2, 0xffffffff); + } + + @Override + public void extractImage(@NotNull final Font font, + final int x, + final int y, + final int width, + final int height, + @NotNull final GuiGraphicsExtractor graphics) + { + graphics.item(this.component.getStack(), x + 2, y + 2); + graphics.itemDecorations(getFont(this.component.getStack()), this.component.getStack(), x + 2, y + 2); } /** diff --git a/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java b/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java index 4aee010adb..89ee89b55b 100644 --- a/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java +++ b/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java @@ -3,8 +3,9 @@ import com.ldtteam.blockui.BOScreen; import com.ldtteam.structurize.client.gui.AbstractBlueprintManipulationWindow; import com.mojang.blaze3d.platform.InputConstants; -import net.minecraft.client.KeyMapping; -import net.minecraft.client.Minecraft; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; import net.neoforged.neoforge.client.settings.IKeyConflictContext; import net.neoforged.neoforge.client.settings.KeyConflictContext; @@ -15,14 +16,15 @@ public class ModKeyMappings { - private static final String CATEGORY = "key.structurize.categories.general"; + private static final KeyMapping.Category CATEGORY = new KeyMapping.Category( + Identifier.fromNamespaceAndPath("structurize", "general")); public static final IKeyConflictContext BLUEPRINT_WINDOW = new IKeyConflictContext() { @Override public boolean isActive() { - if (Minecraft.getInstance().screen instanceof BOScreen screen) + if (Minecraft.getInstance().gui.screen() instanceof BOScreen screen) { return screen.getWindow() instanceof AbstractBlueprintManipulationWindow; } @@ -69,9 +71,10 @@ public boolean conflicts(IKeyConflictContext other) /** * Register key mappings */ - public static void register(@NotNull final RegisterKeyMappingsEvent event) - { - event.register(TELEPORT.get()); + public static void register(@NotNull final RegisterKeyMappingsEvent event) + { + event.registerCategory(CATEGORY); + event.register(TELEPORT.get()); event.register(MOVE_FORWARD.get()); event.register(MOVE_BACK.get()); event.register(MOVE_LEFT.get()); diff --git a/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java b/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java index 9cfbf41c4e..514f8e0398 100644 --- a/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java +++ b/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.client; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.RotationMirror; public record RenderingCacheKey(RotationMirror rotationMirror, Blueprint blueprint) { diff --git a/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java b/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java index ce8fbc7297..019b43e92e 100644 --- a/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java +++ b/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java @@ -1,130 +1,132 @@ -package com.ldtteam.structurize.client; - -import com.ldtteam.common.fakelevel.SingleBlockFakeLevel; -import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; -import com.ldtteam.structurize.component.CapturedBlock; -import com.ldtteam.structurize.items.ItemTagSubstitution; -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.BlockRenderDispatcher; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; -import net.minecraft.core.BlockPos; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.client.NeoForgeRenderTypes; -import net.neoforged.neoforge.client.model.data.ModelData; -import org.jetbrains.annotations.NotNull; - -/** - * Tag anchor renderer; renders replacement block model inside of anchor "overlay" model. - */ -public class TagSubstitutionRenderer extends BlockEntityWithoutLevelRenderer implements BlockEntityRenderer -{ - private static TagSubstitutionRenderer INSTANCE; - - public static TagSubstitutionRenderer getInstance() - { - return INSTANCE; - } - - - private final BlockEntityRendererProvider.Context context; - private SingleBlockFakeLevel renderLevel; - - public TagSubstitutionRenderer(@NotNull final BlockEntityRendererProvider.Context context) - { - super(context.getBlockEntityRenderDispatcher(), context.getModelSet()); - - INSTANCE = this; - this.context = context; - } - - @Override - public void render(@NotNull final BlockEntityTagSubstitution entity, - final float partialTick, - @NotNull final PoseStack poseStack, - @NotNull final MultiBufferSource buffers, - final int packedLight, - final int packedOverlay) - { - final RenderType renderType = NeoForgeRenderTypes.ITEM_LAYERED_TRANSLUCENT.get(); - - render(entity.getReplacement(), entity.getTilePos(), partialTick, poseStack, buffers, packedLight, packedOverlay, renderType); - } - - @Override - public void renderByItem(@NotNull final ItemStack stack, - @NotNull final ItemDisplayContext transformType, - @NotNull final PoseStack poseStack, - @NotNull final MultiBufferSource buffers, - final int packedLight, - final int packedOverlay) - { - final RenderType renderType = NeoForgeRenderTypes.ITEM_LAYERED_TRANSLUCENT.get(); - - if (stack.getItem() instanceof ItemTagSubstitution anchor) - { - this.context.getBlockRenderDispatcher().renderSingleBlock(anchor.getBlock().defaultBlockState(), - poseStack, buffers, packedLight, packedOverlay, ModelData.EMPTY, renderType); - - render(CapturedBlock.readFromItemStack(stack), BlockPos.ZERO, 0, poseStack, buffers, packedLight, packedOverlay, renderType); - } - } - - private void render(@NotNull final CapturedBlock replacement, - @NotNull final BlockPos pos, - final float partialTick, - @NotNull PoseStack poseStack, - @NotNull MultiBufferSource buffers, - final int packedLight, - final int packedOverlay, - @NotNull final RenderType renderType) - { - if (replacement.blockState().isAir()) - { - return; - } - - poseStack.pushPose(); - poseStack.scale(0.995f, 0.995f, 0.995f); - poseStack.translate(0.0025f, 0.0025f, 0.0025f); - - if (replacement.hasBlockEntity()) - { - final BlockEntityRenderDispatcher entityDispatcher = this.context.getBlockEntityRenderDispatcher(); - final Level realLevel = entityDispatcher.level; - if (renderLevel == null) - { - renderLevel = new SingleBlockFakeLevel(realLevel); - } - - renderLevel.withFakeLevelContext(replacement.blockState(), - BlockEntity.loadStatic(BlockPos.ZERO, replacement.blockState(), replacement.serializedBE().get(), realLevel.registryAccess()), - realLevel, - fakeLevel -> { - context.getBlockRenderDispatcher() - .renderSingleBlock(replacement.blockState(), - poseStack, - buffers, - packedLight, - packedOverlay, - renderLevel.getLevelSource().blockEntity.getModelData(), - renderType); - entityDispatcher.render(renderLevel.getLevelSource().blockEntity, partialTick, poseStack, buffers); - }); - } - else - { - context.getBlockRenderDispatcher() - .renderSingleBlock(replacement.blockState(), poseStack, buffers, packedLight, packedOverlay, ModelData.EMPTY, renderType); - } - - poseStack.popPose(); - } -} +package com.ldtteam.structurize.client; + +import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; +import com.mojang.blaze3d.vertex.PoseStack; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.client.renderer.feature.ModelFeatureRenderer; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import net.minecraft.world.phys.Vec3; + +public class TagSubstitutionRenderer implements BlockEntityRenderer +{ + private static TagSubstitutionRenderer instance; + + public static TagSubstitutionRenderer getInstance() + { + return instance; + } + + private final BlockEntityRendererProvider.Context context; + + public TagSubstitutionRenderer(@NotNull final BlockEntityRendererProvider.Context context) + { + instance = this; + this.context = context; + } + + @Override + public State createRenderState() + { + return new State(); + } + + @Override + public void extractRenderState(@NotNull final BlockEntityTagSubstitution entity, + @NotNull final State state, + final float partialTick, + @NotNull final Vec3 cameraPosition, + final ModelFeatureRenderer.CrumblingOverlay breakProgress) + { + BlockEntityRenderState.extractBase(entity, state, breakProgress); + state.partialTick = partialTick; + state.replacement = entity.getReplacement(); + state.tilePos = entity.getTilePos(); + } + + @Override + public void submit(@NotNull final State state, + @NotNull final PoseStack poseStack, + @NotNull final SubmitNodeCollector collector, + @NotNull final CameraRenderState camera) + { + if (state.replacement == null || state.tilePos == null || state.replacement.isEmpty()) + { + return; + } + + poseStack.pushPose(); + poseStack.scale(0.98F, 0.98F, 0.98F); + poseStack.translate(0.01F, 0.01F, 0.01F); + + final BlockEntity replacementEntity = state.replacement.getBlockEntity(state.tilePos); + if (replacementEntity == null) + { + submitBlockModel( + state.replacement.getBlockState(), + state.lightCoords, + net.minecraft.client.renderer.rendertype.RenderTypes.translucentMovingBlock(), + poseStack, + collector); + } + else + { + final BlockEntityRenderState nestedState = context.blockEntityRenderDispatcher() + .tryExtractRenderState(replacementEntity, state.partialTick, null, false); + if (nestedState != null) + { + context.blockEntityRenderDispatcher().submit(nestedState, poseStack, collector, camera); + } + else + { + submitBlockModel( + state.replacement.getBlockState(), + state.lightCoords, + net.minecraft.client.renderer.rendertype.RenderTypes.translucentMovingBlock(), + poseStack, + collector); + } + } + + poseStack.popPose(); + } + + private static void submitBlockModel(final BlockState blockState, + final int packedLight, + @NotNull final RenderType renderType, + @NotNull final PoseStack poseStack, + @NotNull final SubmitNodeCollector collector) + { + if (blockState.getRenderShape() != RenderShape.MODEL) + { + return; + } + + final BlockStateModel model = Minecraft.getInstance().getModelManager().getBlockStateModelSet().get(blockState); + final List parts = new ArrayList<>(); + model.collectParts(BlockAndTintGetter.EMPTY, BlockPos.ZERO, blockState, RandomSource.create(42L), parts); + collector.submitBlockModel(poseStack, renderType, parts, new int[0], packedLight, 0, 0); + } + + public static class State extends BlockEntityRenderState + { + private float partialTick; + private BlockEntityTagSubstitution.ReplacementBlock replacement; + private BlockPos tilePos; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java index e7285b6df9..d08675d9b9 100644 --- a/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java @@ -1,12 +1,10 @@ package com.ldtteam.structurize.client.fakelevel; -import com.ldtteam.common.fakelevel.FakeLevel; -import com.ldtteam.common.fakelevel.IFakeLevelLightProvider; -import com.ldtteam.common.fakelevel.IFakeLevelLightProvider.ConfigBasedLightProvider; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.client.fakelevel.IFakeLevelLightProvider.ConfigBasedLightProvider; import com.ldtteam.structurize.util.BlockUtils; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; @@ -18,7 +16,7 @@ /** * Exists to separate blueprint specific rendering from FakeLevel. */ -public class BlueprintBlockAccess extends FakeLevel +public class BlueprintBlockAccess extends FakeLevel { public static final IFakeLevelLightProvider LIGHT_PROVIDER = new ConfigBasedLightProvider(Structurize.getConfig().getClient().rendererLightLevel); private static final Scoreboard SCOREBOARD = new Scoreboard(); @@ -35,7 +33,7 @@ public class BlueprintBlockAccess extends FakeLevel public BlueprintBlockAccess(final Blueprint blueprint) { - super(blueprint, LIGHT_PROVIDER, Minecraft.getInstance().level, SCOREBOARD, true); + super(blueprint, LIGHT_PROVIDER, SCOREBOARD, true); } private static Level anyLevel() @@ -72,9 +70,9 @@ else if (state.getBlock() == ModBlocks.blockSubstitution.get()) } else if (state.getBlock() == ModBlocks.blockTagSubstitution.get()) { - if (super.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution tag) + if (super.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution tag && !tag.getReplacement().isEmpty()) { - return tag.getReplacement().blockState(); + return tag.getReplacement().getBlockState(); } return Blocks.AIR.defaultBlockState(); } diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java new file mode 100644 index 0000000000..c62e660d75 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java @@ -0,0 +1,752 @@ +package com.ldtteam.structurize.client.fakelevel; + +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData.BlockEntityTagOutput; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeResolver; +import net.minecraft.world.level.biome.Climate.Sampler; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.Heightmap.Types; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureStart; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.ticks.BlackholeTickAccess; +import net.minecraft.world.ticks.TickContainerAccess; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Fake level fake chunk :D all data related methods must redirect to fake level Updating procedure is same as fakeLevel Porting info: + *
    + *
  1. uncomment last method section
  2. + *
  3. fix compile errors
  4. + *
  5. add override for remaining methods and sort/implement them accordingly
  6. + *
  7. comment last method section
  8. + *
+ *

+ */ +public class FakeChunk extends LevelChunk +{ + private final FakeLevel fakeLevel; + + // section cache + int lastY; + LevelChunkSection lastSection = null; + + public FakeChunk(final FakeLevel worldIn, final int x, final int z) + { + super(worldIn, new ChunkPos(x, z)); + this.fakeLevel = worldIn; + + // set itself to cache + fakeLevel.lastX = x; + fakeLevel.lastZ = z; + fakeLevel.lastChunk = this; + } + + // ======================================== + // ========== REDIRECTED METHODS ========== + // ======================================== + + @Override + public BlockState getBlockState(final BlockPos pos) + { + return fakeLevel.getBlockState(pos); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(final BlockPos pos, final EntityCreationType creationMode) + { + return fakeLevel.getBlockEntity(pos); + } + + @Nullable + public BlockEntity getExistingBlockEntity(BlockPos pos) + { + return fakeLevel.getBlockEntity(pos); + } + + @Override + public FluidState getFluidState(final BlockPos pos) + { + return fakeLevel.getFluidState(pos); + } + + @Override + public FluidState getFluidState(final int bx, final int by, final int bz) + { + return getFluidState(new BlockPos(bx, by, bz)); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z) + { + return fakeLevel.getNoiseBiome(x, y, z); + } + + @Override + public Map getBlockEntities() + { + // TODO: this should ideally return only BEs in this chunk + return fakeLevel.blockEntities; + } + + @Override + public Set getBlockEntitiesPos() + { + return getBlockEntities().keySet(); + } + + // ======================================== + // ======= NOOP UNSAFE NULL METHODS ======= + // ======================================== + + // ======================================== + // ========== PERMANENT SETTINGS ========== + // ======================================== + + @Override + public FullChunkStatus getFullStatus() + { + return FullChunkStatus.FULL; + } + + @Override + public ChunkStatus getPersistedStatus() + { + return ChunkStatus.FULL; + } + + @Override + public boolean isUnsaved() + { + return false; + } + + @Override + public boolean isUpgrading() + { + return false; + } + + @Override + public boolean isLightCorrect() + { + return true; + } + + // ======================================== + // ========== HEIGHTMAP RELATED =========== + // ======================================== + + @Override + public int getHeight(Types type, int x, int z) + { + return fakeLevel.getHeight(type, chunkPos.getBlockX(x), chunkPos.getBlockZ(z)); + } + + @Override + public Collection> getHeightmaps() + { + // TODO: investigate.. + return Collections.emptyList(); + } + + @Override + public Heightmap getOrCreateHeightmapUnprimed(Types p_62079_) + { + return null; + } + + @Override + public boolean hasPrimedHeightmap(Types p_187659_) + { + return false; + } + + // ======================================== + // =========== SECTION RELATED ============ + // ======================================== + + @Override + public void findBlocks(java.util.function.Predicate predicate, BiConsumer sink) + { + for (final BlockPos mutablePos : BlockPos.betweenClosed(chunkPos.getBlockX(0), + fakeLevel.levelSource.getMinBuildHeight(), + chunkPos.getBlockZ(0), + Math.min(chunkPos.getBlockX(15), fakeLevel.levelSource.getMaxX() - 1), + fakeLevel.levelSource.getMaxY() - 1, + Math.min(chunkPos.getBlockZ(15), fakeLevel.levelSource.getMaxZ() - 1))) + { + final BlockState blockState = getBlockState(mutablePos); + if (predicate.test(blockState)) + { + sink.accept(mutablePos, blockState); + } + } + } + + @Override + public boolean isYSpaceEmpty(int p_62075_, int p_62076_) + { + return false; + } + + @Override + public LevelChunkSection[] getSections() + { + // don't cache them + return new LevelChunkSection[0]; + } + + @Override + public LevelChunkSection getSection(int yIdx) + { + if (lastY == yIdx && lastSection != null) + { + return lastSection; + } + return new FakeLevelChunkSection(this, yIdx); + } + + // ======================================== + // ============= NOOP METHODS ============= + // ======================================== + + @Override + public void addAndRegisterBlockEntity(BlockEntity p_156391_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public CompoundTag getBlockEntityNbtForSaving(BlockPos position, net.minecraft.core.HolderLookup.Provider provider) + { + // Noop + return null; + } + + @Override + public TickContainerAccess getBlockTicks() + { + // Noop + return BlackholeTickAccess.emptyContainer(); + } + + @Override + public TickContainerAccess getFluidTicks() + { + // Noop + return BlackholeTickAccess.emptyContainer(); + } + + @Override + public void postProcessGeneration(net.minecraft.server.level.ServerLevel level) + { + // Noop + } + + @Override + public void registerAllBlockEntitiesAfterLevelLoad() + { + // Noop + } + + @Override + public void removeBlockEntity(BlockPos p_62919_) + { + // Noop + } + + @Override + public void replaceBiomes(FriendlyByteBuf p_275574_) + { + // Noop + } + + @Override + public void replaceWithPacketData( + FriendlyByteBuf buffer, + Map heightmaps, + Consumer output) + { + // Noop + } + + @Override + public void setBlockEntity(BlockEntity p_156374_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public BlockState setBlockState(BlockPos position, BlockState state, int updateFlags) + { + // Noop + return null; + } + + @Override + public void setFullStatus(Supplier p_62880_) + { + // Noop + } + + @Override + public void unpackTicks(long p_187986_) + { + // Noop + } + + @Override + public void addPackedPostProcess(it.unimi.dsi.fastutil.shorts.ShortList packedPositions, int index) + { + // Noop + } + + @Override + public void addReferenceForStructure(Structure p_223007_, long p_223008_) + { + // Noop + } + + @Override + public void fillBiomesFromNoise(BiomeResolver p_187638_, Sampler p_187639_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public CompoundTag getBlockEntityNbt(BlockPos p_62103_) + { + // Noop, for pending BEs only + return null; + } + + @Override + public void setAllReferences(Map p_187663_) + { + // Noop + } + + @Override + public void setAllStarts(Map p_62090_) + { + // Noop + } + + @Override + public void setBlockEntityNbt(CompoundTag p_62091_) + { + // Noop + } + + @Override + public void setLightCorrect(boolean p_62100_) + { + // Noop + } + + @Override + public void setStartForStructure(Structure p_223010_, StructureStart p_223011_) + { + // Noop + } + + @Override + public void setHeightmap(Types p_62083_, long[] p_62084_) + { + // Noop + } + + // ======================================== + // ======== SUPER IS FINE METHODS ========= + // ======================================== + + /* + @Override + public Level getLevel() + { + return super.getLevel(); + } + + @Override + public void addEntity(Entity p_62826_) + { + super.addEntity(p_62826_); + } + + @Override + public void clearAllBlockEntities() + { + super.clearAllBlockEntities(); + } + + @Override + @javax.annotation.Nullable + public BlockEntity getBlockEntity(BlockPos p_62912_) + { + return super.getBlockEntity(p_62912_); + } + + @Override + public GameEventListenerRegistry getListenerRegistry(int p_251193_) + { + return super.getListenerRegistry(p_251193_); + } + + @Override + public TicksToSave getTicksForSerialization() + { + return super.getTicksForSerialization(); + } + + @Override + public Level getWorldForge() + { + return super.getWorldForge(); + } + + @Override + public boolean isEmpty() + { + return super.isEmpty(); + } + + @Override + public void registerTickContainerInLevel(ServerLevel p_187959_) + { + super.registerTickContainerInLevel(p_187959_); + } + + @Override + public void runPostLoad() + { + super.runPostLoad(); + } + + @Override + public void setLoaded(boolean p_62914_) + { + super.setLoaded(p_62914_); + } + + @Override + public void unregisterTickContainerFromLevel(ServerLevel p_187980_) + { + super.unregisterTickContainerFromLevel(p_187980_); + } + + @Override + public BiomeGenerationSettings carverBiome(Supplier p_223015_) + { + return super.carverBiome(p_223015_); + } + + @Override + public void findBlocks(Predicate p_285343_, BiConsumer p_285030_) + { + super.findBlocks(p_285343_, p_285030_); + } + + @Override + public Map getAllReferences() + { + return super.getAllReferences(); + } + + @Override + public Map getAllStarts() + { + return super.getAllStarts(); + } + + @Override + @javax.annotation.Nullable + public BelowZeroRetrogen getBelowZeroRetrogen() + { + return super.getBelowZeroRetrogen(); + } + + @Override + @javax.annotation.Nullable + public BlendingData getBlendingData() + { + return super.getBlendingData(); + } + + @Override + public int getHeight() + { + return super.getHeight(); + } + + @Override + public LevelHeightAccessor getHeightAccessorForGeneration() + { + return super.getHeightAccessorForGeneration(); + } + + @Override + public int getHighestFilledSectionIndex() + { + return super.getHighestFilledSectionIndex(); + } + + @Override + public ChunkStatus getHighestGeneratedStatus() + { + return super.getHighestGeneratedStatus(); + } + + @Override + public int getHighestSectionPosition() + { + return super.getHighestSectionPosition(); + } + + @Override + public long getInhabitedTime() + { + return super.getInhabitedTime(); + } + + @Override + public int getMinBuildHeight() + { + return super.getMinBuildHeight(); + } + + @Override + public NoiseChunk getOrCreateNoiseChunk(Function p_223013_) + { + return super.getOrCreateNoiseChunk(p_223013_); + } + + @Override + public ChunkPos getPos() + { + return super.getPos(); + } + + @Override + public ShortList[] getPostProcessing() + { + return super.getPostProcessing(); + } + + @Override + public LongSet getReferencesForStructure(Structure p_223017_) + { + return super.getReferencesForStructure(p_223017_); + } + + @Override + public LevelChunkSection getSection(int p_187657_) + { + return super.getSection(p_187657_); + } + + @Override + public ChunkSkyLightSources getSkyLightSources() + { + return super.getSkyLightSources(); + } + + @Override + @javax.annotation.Nullable + public StructureStart getStartForStructure(Structure p_223005_) + { + return super.getStartForStructure(p_223005_); + } + + @Override + public UpgradeData getUpgradeData() + { + return super.getUpgradeData(); + } + + @Override + public boolean hasAnyStructureReferences() + { + return super.hasAnyStructureReferences(); + } + + @Override + public void incrementInhabitedTime(long p_187633_) + { + super.incrementInhabitedTime(p_187633_); + } + + @Override + public void initializeLightSources() + { + super.initializeLightSources(); + } + + @Override + public boolean isOldNoiseGeneration() + { + return super.isOldNoiseGeneration(); + } + + @Override + public void markPosForPostprocessing(BlockPos p_62102_) + { + super.markPosForPostprocessing(p_62102_); + } + + @Override + public void setInhabitedTime(long p_62099_) + { + super.setInhabitedTime(p_62099_); + } + + @Override + public BlockHitResult clip(ClipContext p_45548_) + { + return super.clip(p_45548_); + } + + @Override + @javax.annotation.Nullable + public BlockHitResult clipWithInteractionOverride(Vec3 p_45559_, + Vec3 p_45560_, + BlockPos p_45561_, + VoxelShape p_45562_, + BlockState p_45563_) + { + return super.clipWithInteractionOverride(p_45559_, p_45560_, p_45561_, p_45562_, p_45563_); + } + + @Override + public Optional getBlockEntity(BlockPos p_151367_, BlockEntityType p_151368_) + { + return super.getBlockEntity(p_151367_, p_151368_); + } + + @Override + public double getBlockFloorHeight(BlockPos p_45574_) + { + return super.getBlockFloorHeight(p_45574_); + } + + @Override + public double getBlockFloorHeight(VoxelShape p_45565_, Supplier p_45566_) + { + return super.getBlockFloorHeight(p_45565_, p_45566_); + } + + @Override + public Stream getBlockStates(AABB p_45557_) + { + return super.getBlockStates(p_45557_); + } + + @Override + public int getLightEmission(BlockPos p_45572_) + { + return super.getLightEmission(p_45572_); + } + + @Override + public int getMaxLightLevel() + { + return super.getMaxLightLevel(); + } + + @Override + public BlockHitResult isBlockInLine(ClipBlockStateContext p_151354_) + { + return super.isBlockInLine(p_151354_); + } + + @Override + public int getMaxBuildHeight() + { + return super.getMaxBuildHeight(); + } + + @Override + public int getMaxSection() + { + return super.getMaxSection(); + } + + @Override + public int getMinSection() + { + return super.getMinSection(); + } + + @Override + public int getSectionIndex(int p_151565_) + { + return super.getSectionIndex(p_151565_); + } + + @Override + public int getSectionIndexFromSectionY(int p_151567_) + { + return super.getSectionIndexFromSectionY(p_151567_); + } + + @Override + public int getSectionYFromSectionIndex(int p_151569_) + { + return super.getSectionYFromSectionIndex(p_151569_); + } + + @Override + public int getSectionsCount() + { + return super.getSectionsCount(); + } + + @Override + public boolean isOutsideBuildHeight(BlockPos p_151571_) + { + return super.isOutsideBuildHeight(p_151571_); + } + + @Override + public boolean isOutsideBuildHeight(int p_151563_) + { + return super.isOutsideBuildHeight(p_151563_); + } + + @Override + public @Nullable ModelDataManager getModelDataManager() + { + return super.getModelDataManager(); + } + */ +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunkSource.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunkSource.java new file mode 100644 index 0000000000..8a2c1a95aa --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunkSource.java @@ -0,0 +1,60 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.lighting.LevelLightEngine; +import javax.annotation.Nullable; +import java.util.function.BooleanSupplier; + +/** + * Porting: class is relatively small, just check super class manually (all of missing methods are/were just aliases) + */ +public class FakeChunkSource extends ChunkSource +{ + private final FakeLevel fakeLevel; + + protected FakeChunkSource(final FakeLevel fakeLevel) + { + this.fakeLevel = fakeLevel; + } + + @Override + public FakeLevel getLevel() + { + return fakeLevel; + } + + @Override + @Nullable + public ChunkAccess getChunk(final int x, final int z, final ChunkStatus chunkStatus, final boolean nonNull) + { + return fakeLevel.getChunk(x, z, chunkStatus, nonNull); + } + + @Override + public void tick(final BooleanSupplier p_202162_, final boolean p_202163_) + { + // noop + } + + @Override + public String gatherStats() + { + return fakeLevel.gatherChunkSourceStats(); + } + + @Override + public int getLoadedChunksCount() + { + final int xCount = (fakeLevel.levelSource.getSizeX() + 15) / 16, + zCount = (fakeLevel.levelSource.getSizeZ() + 15) / 16; + return xCount * zCount; + } + + @Override + public LevelLightEngine getLightEngine() + { + return fakeLevel.getLightEngine(); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java new file mode 100644 index 0000000000..377ef5bc52 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java @@ -0,0 +1,1951 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.SectionPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.resources.ResourceKey; +import net.minecraft.core.particles.ExplosionParticleInfo; +import net.minecraft.util.random.WeightedList; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.AbortableIterationConsumer.Continuation; +import net.minecraft.world.TickRateManager; +import net.minecraft.world.clock.ClockManager; +import net.minecraft.world.attribute.EnvironmentAttributeSystem; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageSources; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.item.alchemy.PotionBrewing; +import net.minecraft.world.item.crafting.RecipeAccess; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.ExplosionDamageCalculator; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.FuelValues; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.entity.LevelEntityGetter; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.gameevent.GameEvent.Context; +import net.minecraft.world.level.levelgen.Heightmap.Types; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.ticks.BlackholeTickAccess; +import net.minecraft.world.ticks.LevelTickAccess; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * As much as general fake level. Features: + *

    + *
  • static access to given data
  • + *
  • immutability - disables all external changes (but levelSource can be mutable)
  • + *
  • most of dimension related things is delegated to current client level (class instances can travel accross dimensions)
  • + *
  • biome info is also delegated from client level
  • + *
  • light control - manual or delegated from client level
  • + *
  • primitive chunk and entity management
  • + *
  • basic heightmap support (not fully working yet)
  • + *
  • Few unsafe NPEs methods :)
  • + *
+ *

+ * + * Porting info: + *

    + *
  1. uncomment last method section
  2. + *
  3. fix compile errors
  4. + *
  5. add override for remaining methods and sort/implement them accordingly
  6. + *
  7. comment last method section
  8. + *

+ * + * TODO: extend from client level + */ +public class FakeLevel extends Level +{ + protected IFakeLevelBlockGetter levelSource; + protected final IFakeLevelLightProvider lightProvider; + protected Level realLevel; + protected final Scoreboard scoreboard; + protected final boolean overrideBeLevel; + + protected final FakeChunkSource chunkSource; + protected final FakeLevelLightEngine lightEngine; + protected FakeLevelEntityGetterAdapter levelEntityGetter = FakeLevelEntityGetterAdapter.EMPTY; + private int nextEntityId = 1; + // TODO: this is currently manually filled by class user - ideally if not filled yet this should get constructed from levelSource + // manually + protected Map blockEntities = Collections.emptyMap(); + + /** + * Current rendering worldPos so we can use client level real info + */ + protected BlockPos worldPos = BlockPos.ZERO; + + // chunk cache + int lastX, lastZ; + ChunkAccess lastChunk = null; + + /** + * @param levelSource data source, also try to set block entities/entities collections + * @param lightProvider light source + * @param scoreboard if null client level is used instead + * @param overrideBeLevel if true all block entities will have set level to this instance + * @see #setBlockEntities(Map) for better block entity handling, if set then levelSource BE getter is not used + * @see #setEntities(Collection) only way to add entities into fake level + * @see #setRealLevel(Level) if you want to reuse this instance + */ + public FakeLevel(final IFakeLevelBlockGetter levelSource, + final IFakeLevelLightProvider lightProvider, + @Nullable final Scoreboard scoreboard, + final boolean overrideBeLevel) + { + super(new FakeLevelData(() -> clientLevel(), lightProvider), + clientLevel().dimension(), + clientLevel().registryAccess(), + clientLevel().dimensionTypeRegistration(), + clientLevel().isClientSide(), + false, + 0, + 0); + this.levelSource = levelSource; + this.lightProvider = lightProvider; + this.realLevel = clientLevel(); + this.scoreboard = scoreboard; + this.overrideBeLevel = overrideBeLevel; + this.chunkSource = new FakeChunkSource(this); + this.lightEngine = new FakeLevelLightEngine(this); + + setRealLevel(clientLevel()); + } + + // ======================================== + // ========== FAKE LEVEL METHODS ========== + // ======================================== + + @SuppressWarnings("resource") + protected static ClientLevel clientLevel() + { + return Minecraft.getInstance().level; + } + + public void setRealLevel(final Level realLevel) + { + if (Objects.equals(this.realLevel, realLevel)) + { + return; + } + + if (realLevel != null && realLevel.isClientSide() != this.isClientSide()) + { + throw new IllegalArgumentException("Received wrong sided realLevel - fakeLevel.isClientSide = " + this.isClientSide()); + } + + if (realLevel instanceof final ClientLevel clientLevel) + { + ((FakeLevelData) getLevelData()).vanillaLevel = () -> clientLevel; + } + + this.realLevel = realLevel; + } + + public Level realLevel() + { + return realLevel; + } + + /** + * @param levelSource new data source + */ + public void setLevelSource(final IFakeLevelBlockGetter levelSource) + { + this.levelSource = levelSource; + } + + /** + * @return current data source + */ + public IFakeLevelBlockGetter getLevelSource() + { + return levelSource; + } + + /** + * @param worldPos where is fake level anchor when querying current client level data + */ + public void setWorldPos(final BlockPos worldPos) + { + this.worldPos = worldPos; + } + + /** + * @return anchor in vanilla client level + */ + public BlockPos getWorldPos() + { + return worldPos; + } + + /** + * For better block entity handling in chunk methods. If set then {@link IFakeLevelBlockGetter#getBlockEntity(BlockPos) + * levelSource.getBlockEntity(BlockPos)} is not used. Reset with empty collection + * + * @param blockEntities all block entities, should be data equivalent to levelSource + */ + public void setBlockEntities(final Map blockEntities) + { + this.blockEntities = blockEntities; + } + + /** + * @param entities all entities, their level should be this fake level instance. Reset with empty collection + */ + public void setEntities(final Collection entities) + { + levelEntityGetter = entities.isEmpty() ? FakeLevelEntityGetterAdapter.EMPTY : FakeLevelEntityGetterAdapter.ofEntities(entities); + } + + /** + * Minecraft 26.2 requires every entity to receive a non-zero id before it + * is registered with an {@link net.minecraft.world.level.entity.EntityLookup}. + * The base client/server level implementations provide that allocator, but + * the immutable fake level inherits the base fallback (zero). Blueprint + * entities are created in this level, so give them ids at construction time + * just like a real level would. + */ + @Override + public int getNextEntityId() + { + return nextEntityId++; + } + + // ======================================== + // ======= CTOR REAL LEVEL REDIRECTS ====== + // ======================================== + // Note: must have null check because super ctor + + @Override + public ResourceKey dimension() + { + return realLevel() != null ? realLevel().dimension() : super.dimension(); + } + + @Override + public RegistryAccess registryAccess() + { + return realLevel() != null ? realLevel().registryAccess() : super.registryAccess(); + } + + @Override + public DamageSources damageSources() + { + return realLevel() != null ? realLevel().damageSources() : super.damageSources(); + } + + @Override + public DimensionType dimensionType() + { + return realLevel() != null ? realLevel().dimensionType() : super.dimensionType(); + } + + @Override + public Holder dimensionTypeRegistration() + { + return realLevel() != null ? realLevel().dimensionTypeRegistration() : super.dimensionTypeRegistration(); + } + + @Override + public WorldBorder getWorldBorder() + { + final Level level = realLevel(); + return level != null ? level.getWorldBorder() : new WorldBorder(); + } + + // ======================================== + // ========== REDIRECTED METHODS ========== + // ======================================== + + @Nullable + @Override + public BlockEntity getBlockEntity(final BlockPos pos) + { + final BlockEntity blockEntity = blockEntities.isEmpty() ? levelSource.getBlockEntity(pos) : blockEntities.get(pos); + if (blockEntity != null && blockEntity.getLevel() != this && (overrideBeLevel || !blockEntity.hasLevel())) + { + blockEntity.setLevel(this); + } + return blockEntity; + } + + @Nullable + public BlockEntity getExistingBlockEntity(BlockPos pos) + { + return getBlockEntity(pos); + } + + @Override + public BlockState getBlockState(final BlockPos pos) + { + return levelSource.isPosInside(pos) ? levelSource.getBlockState(pos) : Blocks.AIR.defaultBlockState(); + } + + @Override + public ChunkAccess getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull) + { + if (lastX == x && lastZ == z && lastChunk != null) + { + return lastChunk; + } + return nonnull || hasChunk(x, z) ? new FakeChunk(this, x, z) : null; + } + + @Override + public boolean hasChunk(int chunkX, int chunkZ) + { + final int posX = SectionPos.sectionToBlockCoord(chunkX); + final int posZ = SectionPos.sectionToBlockCoord(chunkZ); + return levelSource.getMinX() <= posX && posX < levelSource.getMaxX() && + levelSource.getMinZ() <= posZ && + posZ < levelSource.getMaxZ(); + } + + @Override + public int getBrightness(final LightLayer lightType, final BlockPos pos) + { + return lightProvider.forceOwnLightLevel() ? lightProvider.getBrightness(lightType, pos) : + realLevel().getBrightness(lightType, worldPos.offset(pos)); + } + + @Override + public int getRawBrightness(BlockPos pos, int amount) + { + return lightProvider.forceOwnLightLevel() ? lightProvider.getRawBrightness(pos, amount) : + realLevel().getRawBrightness(worldPos.offset(pos), amount); + } + + @Override + public int getSkyDarken() + { + return lightProvider.forceOwnLightLevel() ? lightProvider.getSkyDarken() : realLevel().getSkyDarken(); + } + + @Override + public Scoreboard getScoreboard() + { + return scoreboard == null ? realLevel().getScoreboard() : scoreboard; + } + + @Override + public FluidState getFluidState(final BlockPos pos) + { + return levelSource.getFluidState(pos); + } + + @Override + public int getHeight() + { + return levelSource.getHeight(); + } + + @Override + public int getMinY() + { + return levelSource.getMinY(); + } + + public int getMinBuildHeight() + { + return getMinY(); + } + + @Override + public boolean isInWorldBounds(final BlockPos pos) + { + return levelSource.isPosInside(pos); + } + + @Override + public CrashReportCategory fillReportDetails(CrashReport report) + { + CrashReportCategory crashreportcategory = report.addCategory("Structurize fake level"); + levelSource.describeSelfInCrashReport(crashreportcategory); + return crashreportcategory; + } + + @Override + protected LevelEntityGetter getEntities() + { + return levelEntityGetter; + } + + @Override + @javax.annotation.Nullable + public Entity getEntity(int id) + { + return levelEntityGetter.get(id); + } + + @Override + public List players() + { + final List result = new ArrayList<>(); + levelEntityGetter.get(EntityTypeTest.forClass(AbstractClientPlayer.class), player -> { + result.add(player); + return Continuation.CONTINUE; + }); + return result; + } + + @Override + public int getHeight(Types heightmapType, int x, int z) + { + final MutableBlockPos pos = new MutableBlockPos(x, levelSource.getMinBuildHeight(), z); + + if (levelSource.isPosInside(pos)) + { + for (int y = levelSource.getMaxY() - 1; y >= levelSource.getMinY(); y--) + { + pos.setY(y); + if (heightmapType.isOpaque().test(levelSource.getBlockState(pos))) + { + return y; + } + } + } + + return levelSource.getMinBuildHeight(); + } + + @Override + public ChunkSource getChunkSource() + { + return chunkSource; + } + + @Override + public LevelLightEngine getLightEngine() + { + return lightEngine; + } + + @Override + public String gatherChunkSourceStats() + { + return "Fake level for: " + levelSource; + } + + @Override + public Holder getBiome(BlockPos pos) + { + return realLevel().getBiome(worldPos.offset(pos)); + } + + @Override + public BiomeManager getBiomeManager() + { + return realLevel().getBiomeManager(); + } + + @Override + public RecipeAccess recipeAccess() + { + return realLevel().recipeAccess(); + } + + @Override + public TickRateManager tickRateManager() + { + return realLevel().tickRateManager(); + } + + @Override + public ClockManager clockManager() + { + return realLevel().clockManager(); + } + + @Override + public EnvironmentAttributeSystem environmentAttributes() + { + return realLevel().environmentAttributes(); + } + + @Override + public PotionBrewing potionBrewing() + { + return realLevel().potionBrewing(); + } + + @Override + public FuelValues fuelValues() + { + return realLevel().fuelValues(); + } + + @Override + public void setRespawnData(final LevelData.RespawnData respawnData) + { + realLevel().setRespawnData(respawnData); + } + + @Override + public LevelData.RespawnData getRespawnData() + { + return realLevel().getRespawnData(); + } + + @Override + public Collection> dragonParts() + { + return realLevel().dragonParts(); + } + + @Override + public FeatureFlagSet enabledFeatures() + { + return realLevel().enabledFeatures(); + } + + @Override + public Holder getUncachedNoiseBiome(int x, int y, int z) + { + return realLevel().getUncachedNoiseBiome(x, y, z); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z) + { + return realLevel().getNoiseBiome(x, y, z); + } + + // ======================================== + // ======= NOOP UNSAFE NULL METHODS ======= + // ======================================== + + @Override + public void explode(@javax.annotation.Nullable Entity entity, + @javax.annotation.Nullable DamageSource damageSource, + @javax.annotation.Nullable ExplosionDamageCalculator calculator, + double x, + double y, + double z, + float radius, + boolean fire, + ExplosionInteraction interaction, + ParticleOptions smallParticles, + ParticleOptions largeParticles, + WeightedList particleInfo, + Holder sound) + { + throw new UnsupportedOperationException("Structurize fake immutable level - no explosions possible!"); + } + + // ======================================== + // ========== PERMANENT SETTINGS ========== + // ======================================== + + @Override + public boolean isLoaded(BlockPos pos) + { + // Noop + return true; + } + + @Override + public float getRainLevel(float delta) + { + // Noop + return 0; + } + + @Override + public float getThunderLevel(float delta) + { + // Noop + return 0; + } + + @Override + public boolean isRainingAt(BlockPos position) + { + return isRaining(); + } + + @Override + public boolean noSave() + { + // Noop + return true; + } + + @Override + public int getSeaLevel() + { + return 0; + } + + // ======================================== + // ============ NOOP OVERRIDES ============ + // ======================================== + + @Override + public void destroyBlockProgress(int p_46506_, BlockPos p_46507_, int p_46508_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public MapItemSavedData getMapData(net.minecraft.world.level.saveddata.maps.MapId id) + { + // Noop + return null; + } + + @Override + public void playSeededSound(@javax.annotation.Nullable Entity source, + Entity p_220373_, + Holder p_263500_, + SoundSource p_220375_, + float p_220376_, + float p_220377_, + long p_220378_) + { + // Noop + } + + @Override + public void playSeededSound(@javax.annotation.Nullable Entity source, + double p_263004_, + double p_263398_, + double p_263376_, + Holder p_263359_, + SoundSource p_263020_, + float p_263055_, + float p_262914_, + long p_262991_) + { + // Noop + } + + @Override + public void sendBlockUpdated(BlockPos p_46612_, BlockState p_46613_, BlockState p_46614_, int p_46615_) + { + // Noop + } + + @Override + public void gameEvent(Holder event, Vec3 position, Context context) + { + // Noop + } + + @Override + public LevelTickAccess getBlockTicks() + { + // Noop + return BlackholeTickAccess.emptyLevelList(); + } + + @Override + public LevelTickAccess getFluidTicks() + { + // Noop + return BlackholeTickAccess.emptyLevelList(); + } + + @Override + public void levelEvent(@javax.annotation.Nullable Entity source, int eventId, BlockPos position, int data) + { + // Noop + } + + // ======================================== + // ============= NOOP METHODS ============= + // ======================================== + + @Override + public void addBlockEntityTicker(TickingBlockEntity p_151526_) + { + // Noop + } + + @Override + public void addFreshBlockEntities(Collection beList) + { + // Noop + } + + @Override + public void blockEvent(BlockPos p_46582_, Block p_46583_, int p_46584_, int p_46585_) + { + // Noop + } + + @Override + public void close() throws IOException + { + // Noop + } + + @Override + public boolean destroyBlock(BlockPos p_46626_, boolean p_46627_, @javax.annotation.Nullable Entity p_46628_, int p_46629_) + { + // Noop + return false; + } + + @Override + public void markAndNotifyBlock(BlockPos p_46605_, + @javax.annotation.Nullable LevelChunk levelchunk, + BlockState blockstate, + BlockState p_46606_, + int p_46607_, + int p_46608_) + { + // Noop + } + + @Override + public boolean mayInteract(Entity entity, BlockPos position) + { + // Noop + return false; + } + + @Override + public void neighborShapeChanged(Direction direction, + BlockPos position, + BlockPos neighborPosition, + BlockState neighborState, + int updateFlags, + int updateLimit) + { + // Noop + } + + @Override + public boolean removeBlock(BlockPos p_46623_, boolean p_46624_) + { + return false; + } + + @Override + public boolean setBlock(BlockPos p_46605_, BlockState p_46606_, int p_46607_, int p_46608_) + { + // Noop + return false; + } + + @Override + public void setRainLevel(float p_46735_) + { + // Noop + } + + @Override + public void setSpawnSettings(boolean spawnEnemies) + { + // Noop + } + + @Override + public void setThunderLevel(float p_46708_) + { + // Noop + } + + @Override + public boolean shouldTickBlocksAt(long p_186456_) + { + // Noop + return false; + } + + @Override + public boolean shouldTickDeath(Entity p_186458_) + { + // Noop + return false; + } + + @Override + public void tickBlockEntities() + { + // Noop + } + + @Override + public void updateNeighborsAt(BlockPos p_46673_, Block p_46674_) + { + // Noop + } + + @Override + public void updateSkyBrightness() + { + // Noop + } + + // ======================================== + // ======== SUPER IS FINE METHODS ========= + // ======================================== + + /* + @Override + public void removeBlockEntity(BlockPos p_46748_) + { + super.removeBlockEntity(p_46748_); + } + + @Override + public void addAlwaysVisibleParticle(ParticleOptions p_46684_, + double p_46685_, + double p_46686_, + double p_46687_, + double p_46688_, + double p_46689_, + double p_46690_) + { + super.addAlwaysVisibleParticle(p_46684_, p_46685_, p_46686_, p_46687_, p_46688_, p_46689_, p_46690_); + } + + @Override + public void addAlwaysVisibleParticle(ParticleOptions p_46691_, + boolean p_46692_, + double p_46693_, + double p_46694_, + double p_46695_, + double p_46696_, + double p_46697_, + double p_46698_) + { + super.addAlwaysVisibleParticle(p_46691_, p_46692_, p_46693_, p_46694_, p_46695_, p_46696_, p_46697_, p_46698_); + } + + @Override + public void addDestroyBlockEffect(BlockPos p_151531_, BlockState p_151532_) + { + super.addDestroyBlockEffect(p_151531_, p_151532_); + } + + @Override + public void addParticle(ParticleOptions p_46631_, + double p_46632_, + double p_46633_, + double p_46634_, + double p_46635_, + double p_46636_, + double p_46637_) + { + super.addParticle(p_46631_, p_46632_, p_46633_, p_46634_, p_46635_, p_46636_, p_46637_); + } + + @Override + public void addParticle(ParticleOptions p_46638_, + boolean p_46639_, + double p_46640_, + double p_46641_, + double p_46642_, + double p_46643_, + double p_46644_, + double p_46645_) + { + super.addParticle(p_46638_, p_46639_, p_46640_, p_46641_, p_46642_, p_46643_, p_46644_, p_46645_); + } + + @Override + public void blockEntityChanged(BlockPos p_151544_) + { + super.blockEntityChanged(p_151544_); + } + + @Override + public void broadcastDamageEvent(Entity p_270831_, DamageSource p_270361_) + { + super.broadcastDamageEvent(p_270831_, p_270361_); + } + + @Override + public void broadcastEntityEvent(Entity p_46509_, byte p_46510_) + { + super.broadcastEntityEvent(p_46509_, p_46510_); + } + + @Override + public void createFireworks(double p_46475_, + double p_46476_, + double p_46477_, + double p_46478_, + double p_46479_, + double p_46480_, + @javax.annotation.Nullable CompoundTag p_46481_) + { + super.createFireworks(p_46475_, p_46476_, p_46477_, p_46478_, p_46479_, p_46480_, p_46481_); + } + + @Override + public ResourceKey dimensionTypeId() + { + return super.dimensionTypeId(); + } + + @Override + public void disconnect() + { + super.disconnect(); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_256599_, + double p_255914_, + double p_255684_, + double p_255843_, + float p_256310_, + ExplosionInteraction p_256178_) + { + return super.explode(p_256599_, p_255914_, p_255684_, p_255843_, p_256310_, p_256178_); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_255682_, + double p_255803_, + double p_256403_, + double p_256538_, + float p_255674_, + boolean p_256634_, + ExplosionInteraction p_256111_) + { + return super.explode(p_255682_, p_255803_, p_256403_, p_256538_, p_255674_, p_256634_, p_256111_); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_255653_, + @javax.annotation.Nullable DamageSource p_256558_, + @javax.annotation.Nullable ExplosionDamageCalculator p_255929_, + Vec3 p_256001_, + float p_255963_, + boolean p_256099_, + ExplosionInteraction p_256371_) + { + return super.explode(p_255653_, p_256558_, p_255929_, p_256001_, p_255963_, p_256099_, p_256371_); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_256145_, + @javax.annotation.Nullable DamageSource p_256004_, + @javax.annotation.Nullable ExplosionDamageCalculator p_255696_, + double p_256208_, + double p_256036_, + double p_255746_, + float p_256647_, + boolean p_256098_, + ExplosionInteraction p_256104_) + { + return super.explode(p_256145_, p_256004_, p_255696_, p_256208_, p_256036_, p_255746_, p_256647_, p_256098_, p_256104_); + } + + @Override + public BlockPos getBlockRandomPos(int p_46497_, int p_46498_, int p_46499_, int p_46500_) + { + return super.getBlockRandomPos(p_46497_, p_46498_, p_46499_, p_46500_); + } + + @Override + public LevelChunk getChunk(int p_46727_, int p_46728_) + { + return super.getChunk(p_46727_, p_46728_); + } + + @Override + public LevelChunk getChunkAt(BlockPos p_46746_) + { + return super.getChunkAt(p_46746_); + } + + @Override + @javax.annotation.Nullable + public BlockGetter getChunkForCollisions(int p_46711_, int p_46712_) + { + return super.getChunkForCollisions(p_46711_, p_46712_); + } + + @Override + public DifficultyInstance getCurrentDifficultyAt(BlockPos p_46730_) + { + return super.getCurrentDifficultyAt(p_46730_); + } + + @Override + public long getDayTime() + { + return super.getDayTime(); + } + + @Override + public List getEntities(@javax.annotation.Nullable Entity p_46536_, AABB p_46537_, Predicate p_46538_) + { + return super.getEntities(p_46536_, p_46537_, p_46538_); + } + + @Override + public List getEntities(EntityTypeTest p_151528_, AABB p_151529_, Predicate p_151530_) + { + return super.getEntities(p_151528_, p_151529_, p_151530_); + } + + @Override + public void getEntities(EntityTypeTest p_261899_, + AABB p_261837_, + Predicate p_261519_, + List p_262046_) + { + super.getEntities(p_261899_, p_261837_, p_261519_, p_262046_); + } + + @Override + public void getEntities(EntityTypeTest p_261885_, + AABB p_262086_, + Predicate p_261688_, + List p_262071_, + int p_261858_) + { + super.getEntities(p_261885_, p_262086_, p_261688_, p_262071_, p_261858_); + } + + @Override + public GameRules getGameRules() + { + return super.getGameRules(); + } + + @Override + public long getGameTime() + { + return super.getGameTime(); + } + + @Override + public LevelData getLevelData() + { + return super.getLevelData(); + } + + @Override + public double getMaxEntityRadius() + { + return super.getMaxEntityRadius(); + } + + @Override + public RandomSource getRandom() + { + return super.getRandom(); + } + + @Override + @javax.annotation.Nullable + public MinecraftServer getServer() + { + return super.getServer(); + } + + @Override + public float getSharedSpawnAngle() + { + return super.getSharedSpawnAngle(); + } + + @Override + public BlockPos getSharedSpawnPos() + { + return super.getSharedSpawnPos(); + } + + @Override + public float getSunAngle(float p_46491_) + { + return super.getSunAngle(p_46491_); + } + + @Override + public void globalLevelEvent(int p_46665_, BlockPos p_46666_, int p_46667_) + { + super.globalLevelEvent(p_46665_, p_46666_, p_46667_); + } + + @Override + public void guardEntityTick(Consumer p_46654_, T p_46655_) + { + super.guardEntityTick(p_46654_, p_46655_); + } + + @Override + public double increaseMaxEntityRadius(double value) + { + return super.increaseMaxEntityRadius(value); + } + + @Override + public boolean isClientSide() + { + return super.isClientSide(); + } + + @Override + public boolean isFluidAtPosition(BlockPos p_151541_, Predicate p_151542_) + { + return super.isFluidAtPosition(p_151541_, p_151542_); + } + + @Override + public boolean isNight() + { + return super.isNight(); + } + + @Override + public boolean isRaining() + { + return super.isRaining(); + } + + @Override + public boolean isStateAtPosition(BlockPos p_46620_, Predicate p_46621_) + { + return super.isStateAtPosition(p_46620_, p_46621_); + } + + @Override + public boolean isThundering() + { + return super.isThundering(); + } + + @Override + public boolean loadedAndEntityCanStandOn(BlockPos p_46576_, Entity p_46577_) + { + return super.loadedAndEntityCanStandOn(p_46576_, p_46577_); + } + + @Override + public boolean loadedAndEntityCanStandOnFace(BlockPos p_46579_, Entity p_46580_, Direction p_46581_) + { + return super.loadedAndEntityCanStandOnFace(p_46579_, p_46580_, p_46581_); + } + + @Override + public void neighborChanged(BlockPos p_46587_, Block p_46588_, BlockPos p_46589_) + { + super.neighborChanged(p_46587_, p_46588_, p_46589_); + } + + @Override + public void neighborChanged(BlockState p_220379_, BlockPos p_220380_, Block p_220381_, BlockPos p_220382_, boolean p_220383_) + { + super.neighborChanged(p_220379_, p_220380_, p_220381_, p_220382_, p_220383_); + } + + @Override + public long nextSubTickCount() + { + return super.nextSubTickCount(); + } + + @Override + public void onBlockStateChange(BlockPos p_46609_, BlockState p_46610_, BlockState p_46611_) + { + super.onBlockStateChange(p_46609_, p_46610_, p_46611_); + } + + @Override + public void playLocalSound(BlockPos p_250938_, + SoundEvent p_252209_, + SoundSource p_249161_, + float p_249980_, + float p_250277_, + boolean p_250151_) + { + super.playLocalSound(p_250938_, p_252209_, p_249161_, p_249980_, p_250277_, p_250151_); + } + + @Override + public void playLocalSound(double p_46482_, + double p_46483_, + double p_46484_, + SoundEvent p_46485_, + SoundSource p_46486_, + float p_46487_, + float p_46488_, + boolean p_46489_) + { + super.playLocalSound(p_46482_, p_46483_, p_46484_, p_46485_, p_46486_, p_46487_, p_46488_, p_46489_); + } + + @Override + public void playSeededSound(@javax.annotation.Nullable Player p_220363_, + double p_220364_, + double p_220365_, + double p_220366_, + SoundEvent p_220367_, + SoundSource p_220368_, + float p_220369_, + float p_220370_, + long p_220371_) + { + super.playSeededSound(p_220363_, p_220364_, p_220365_, p_220366_, p_220367_, p_220368_, p_220369_, p_220370_, p_220371_); + } + + @Override + public void playSound(@javax.annotation.Nullable Entity p_252137_, + BlockPos p_251749_, + SoundEvent p_248842_, + SoundSource p_251104_, + float p_249531_, + float p_250763_) + { + super.playSound(p_252137_, p_251749_, p_248842_, p_251104_, p_249531_, p_250763_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_46560_, + BlockPos p_46561_, + SoundEvent p_46562_, + SoundSource p_46563_, + float p_46564_, + float p_46565_) + { + super.playSound(p_46560_, p_46561_, p_46562_, p_46563_, p_46564_, p_46565_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_46551_, + Entity p_46552_, + SoundEvent p_46553_, + SoundSource p_46554_, + float p_46555_, + float p_46556_) + { + super.playSound(p_46551_, p_46552_, p_46553_, p_46554_, p_46555_, p_46556_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_46543_, + double p_46544_, + double p_46545_, + double p_46546_, + SoundEvent p_46547_, + SoundSource p_46548_, + float p_46549_, + float p_46550_) + { + super.playSound(p_46543_, p_46544_, p_46545_, p_46546_, p_46547_, p_46548_, p_46549_, p_46550_); + } + + @Override + protected void prepareWeather() + { + super.prepareWeather(); + } + + @Override + public void sendPacketToServer(Packet p_46657_) + { + super.sendPacketToServer(p_46657_); + } + + @Override + public boolean setBlock(BlockPos p_46601_, BlockState p_46602_, int p_46603_) + { + return super.setBlock(p_46601_, p_46602_, p_46603_); + } + + @Override + public boolean setBlockAndUpdate(BlockPos p_46598_, BlockState p_46599_) + { + return super.setBlockAndUpdate(p_46598_, p_46599_); + } + + @Override + public void setBlocksDirty(BlockPos p_46678_, BlockState p_46679_, BlockState p_46680_) + { + super.setBlocksDirty(p_46678_, p_46679_, p_46680_); + } + + @Override + public void setSkyFlashTime(int p_46709_) + { + super.setSkyFlashTime(p_46709_); + } + + @Override + public void setBlockEntity(BlockEntity p_151524_) + { + super.setBlockEntity(p_151524_); + } + + @Override + public boolean shouldTickBlocksAt(BlockPos p_220394_) + { + return super.shouldTickBlocksAt(p_220394_); + } + + @Override + public void updateNeighborsAtExceptFromFacing(BlockPos p_46591_, Block p_46592_, Direction p_46593_) + { + super.updateNeighborsAtExceptFromFacing(p_46591_, p_46592_, p_46593_); + } + + @Override + public void blockUpdated(BlockPos p_46781_, Block p_46782_) + { + super.blockUpdated(p_46781_, p_46782_); + } + + @Override + public long dayTime() + { + return super.dayTime(); + } + + @Override + public void gameEvent(@javax.annotation.Nullable Entity p_220401_, GameEvent p_220402_, Vec3 p_220403_) + { + super.gameEvent(p_220401_, p_220402_, p_220403_); + } + + @Override + public void gameEvent(@javax.annotation.Nullable Entity p_151549_, GameEvent p_151550_, BlockPos p_151551_) + { + super.gameEvent(p_151549_, p_151550_, p_151551_); + } + + @Override + public void gameEvent(GameEvent p_220408_, BlockPos p_220409_, Context p_220410_) + { + super.gameEvent(p_220408_, p_220409_, p_220410_); + } + + @Override + public Difficulty getDifficulty() + { + return super.getDifficulty(); + } + + @Override + public void levelEvent(int p_46797_, BlockPos p_46798_, int p_46799_) + { + super.levelEvent(p_46797_, p_46798_, p_46799_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_251195_, BlockPos p_250192_, SoundEvent p_249887_, SoundSource p_250593_) + { + super.playSound(p_251195_, p_250192_, p_249887_, p_250593_); + } + + @Override + public void scheduleTick(BlockPos p_186461_, Block p_186462_, int p_186463_) + { + super.scheduleTick(p_186461_, p_186462_, p_186463_); + } + + @Override + public void scheduleTick(BlockPos p_186470_, Fluid p_186471_, int p_186472_) + { + super.scheduleTick(p_186470_, p_186471_, p_186472_); + } + + @Override + public void scheduleTick(BlockPos p_186465_, Block p_186466_, int p_186467_, TickPriority p_186468_) + { + super.scheduleTick(p_186465_, p_186466_, p_186467_, p_186468_); + } + + @Override + public void scheduleTick(BlockPos p_186474_, Fluid p_186475_, int p_186476_, TickPriority p_186477_) + { + super.scheduleTick(p_186474_, p_186475_, p_186476_, p_186477_); + } + + @Override + public Optional getBlockEntity(BlockPos p_151452_, BlockEntityType p_151453_) + { + return super.getBlockEntity(p_151452_, p_151453_); + } + + @Override + public List getEntityCollisions(@javax.annotation.Nullable Entity p_186447_, AABB p_186448_) + { + return super.getEntityCollisions(p_186447_, p_186448_); + } + + @Override + public BlockPos getHeightmapPos(Types p_45831_, BlockPos p_45832_) + { + return super.getHeightmapPos(p_45831_, p_45832_); + } + + @Override + public boolean isUnobstructed(@javax.annotation.Nullable Entity p_45828_, VoxelShape p_45829_) + { + return super.isUnobstructed(p_45828_, p_45829_); + } + + @Override + public List getEntities(@javax.annotation.Nullable Entity p_45934_, AABB p_45935_) + { + return super.getEntities(p_45934_, p_45935_); + } + + @Override + public List getEntitiesOfClass(Class p_45977_, AABB p_45978_) + { + return super.getEntitiesOfClass(p_45977_, p_45978_); + } + + @Override + public List getEntitiesOfClass(Class p_45979_, AABB p_45980_, Predicate p_45981_) + { + return super.getEntitiesOfClass(p_45979_, p_45980_, p_45981_); + } + + @Override + public List getNearbyEntities(Class p_45972_, + TargetingConditions p_45973_, + LivingEntity p_45974_, + AABB p_45975_) + { + return super.getNearbyEntities(p_45972_, p_45973_, p_45974_, p_45975_); + } + + @Override + public List getNearbyPlayers(TargetingConditions p_45956_, LivingEntity p_45957_, AABB p_45958_) + { + return super.getNearbyPlayers(p_45956_, p_45957_, p_45958_); + } + + @Override + @javax.annotation.Nullable + public T getNearestEntity(List p_45983_, + TargetingConditions p_45984_, + @javax.annotation.Nullable LivingEntity p_45985_, + double p_45986_, + double p_45987_, + double p_45988_) + { + return super.getNearestEntity(p_45983_, p_45984_, p_45985_, p_45986_, p_45987_, p_45988_); + } + + @Override + @javax.annotation.Nullable + public T getNearestEntity(Class p_45964_, + TargetingConditions p_45965_, + @javax.annotation.Nullable LivingEntity p_45966_, + double p_45967_, + double p_45968_, + double p_45969_, + AABB p_45970_) + { + return super.getNearestEntity(p_45964_, p_45965_, p_45966_, p_45967_, p_45968_, p_45969_, p_45970_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(Entity p_45931_, double p_45932_) + { + return super.getNearestPlayer(p_45931_, p_45932_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(TargetingConditions p_45947_, LivingEntity p_45948_) + { + return super.getNearestPlayer(p_45947_, p_45948_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(TargetingConditions p_45942_, double p_45943_, double p_45944_, double p_45945_) + { + return super.getNearestPlayer(p_45942_, p_45943_, p_45944_, p_45945_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(double p_45919_, + double p_45920_, + double p_45921_, + double p_45922_, + @javax.annotation.Nullable Predicate p_45923_) + { + return super.getNearestPlayer(p_45919_, p_45920_, p_45921_, p_45922_, p_45923_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(double p_45925_, double p_45926_, double p_45927_, double p_45928_, boolean p_45929_) + { + return super.getNearestPlayer(p_45925_, p_45926_, p_45927_, p_45928_, p_45929_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(TargetingConditions p_45950_, + LivingEntity p_45951_, + double p_45952_, + double p_45953_, + double p_45954_) + { + return super.getNearestPlayer(p_45950_, p_45951_, p_45952_, p_45953_, p_45954_); + } + + @Override + @javax.annotation.Nullable + public Player getPlayerByUUID(UUID p_46004_) + { + return super.getPlayerByUUID(p_46004_); + } + + @Override + public boolean hasNearbyAlivePlayer(double p_45915_, double p_45916_, double p_45917_, double p_45918_) + { + return super.hasNearbyAlivePlayer(p_45915_, p_45916_, p_45917_, p_45918_); + } + + @Override + public boolean canSeeSkyFromBelowWater(BlockPos p_46862_) + { + return super.canSeeSkyFromBelowWater(p_46862_); + } + + @Override + public boolean containsAnyLiquid(AABB p_46856_) + { + return super.containsAnyLiquid(p_46856_); + } + + @Override + public Stream getBlockStatesIfLoaded(AABB p_46848_) + { + return super.getBlockStatesIfLoaded(p_46848_); + } + + @Override + public int getBlockTint(BlockPos p_46836_, ColorResolver p_46837_) + { + return super.getBlockTint(p_46836_, p_46837_); + } + + @Override + public ChunkAccess getChunk(BlockPos p_46866_) + { + return super.getChunk(p_46866_); + } + + @Override + public ChunkAccess getChunk(int p_46820_, int p_46821_, ChunkStatus p_46822_) + { + return super.getChunk(p_46820_, p_46821_, p_46822_); + } + + @Override + public float getLightLevelDependentMagicValue(BlockPos p_220418_) + { + return super.getLightLevelDependentMagicValue(p_220418_); + } + + @Override + public int getMaxLocalRawBrightness(BlockPos p_46804_) + { + return super.getMaxLocalRawBrightness(p_46804_); + } + + @Override + public int getMaxLocalRawBrightness(BlockPos p_46850_, int p_46851_) + { + return super.getMaxLocalRawBrightness(p_46850_, p_46851_); + } + + @Override + public void updateNeighbourForOutputSignal(BlockPos p_46718_, Block p_46719_) + { + super.updateNeighbourForOutputSignal(p_46718_, p_46719_); + } + + @Override + public float getPathfindingCostFromLightLevels(BlockPos p_220420_) + { + return super.getPathfindingCostFromLightLevels(p_220420_); + } + + @Override + public boolean hasChunkAt(BlockPos p_46806_) + { + return super.hasChunkAt(p_46806_); + } + + @Override + public boolean hasChunkAt(int p_151578_, int p_151579_) + { + return super.hasChunkAt(p_151578_, p_151579_); + } + + @Override + public boolean hasChunksAt(BlockPos p_46833_, BlockPos p_46834_) + { + return super.hasChunksAt(p_46833_, p_46834_); + } + + @Override + public boolean hasChunksAt(int p_151573_, int p_151574_, int p_151575_, int p_151576_) + { + return super.hasChunksAt(p_151573_, p_151574_, p_151575_, p_151576_); + } + + @Override + public boolean hasChunksAt(int p_46813_, int p_46814_, int p_46815_, int p_46816_, int p_46817_, int p_46818_) + { + return super.hasChunksAt(p_46813_, p_46814_, p_46815_, p_46816_, p_46817_, p_46818_); + } + + @Override + public HolderLookup holderLookup(ResourceKey> p_249578_) + { + return super.holderLookup(p_249578_); + } + + @Override + public boolean isAreaLoaded(BlockPos center, int range) + { + return super.isAreaLoaded(center, range); + } + + @Override + public boolean isEmptyBlock(BlockPos p_46860_) + { + return super.isEmptyBlock(p_46860_); + } + + @Override + public boolean isWaterAt(BlockPos p_46802_) + { + return super.isWaterAt(p_46802_); + } + + @Override + public boolean canSeeSky(BlockPos p_45528_) + { + return super.canSeeSky(p_45528_); + } + + @Override + public BlockHitResult clip(ClipContext p_45548_) + { + return super.clip(p_45548_); + } + + @Override + @javax.annotation.Nullable + public BlockHitResult clipWithInteractionOverride(Vec3 p_45559_, + Vec3 p_45560_, + BlockPos p_45561_, + VoxelShape p_45562_, + BlockState p_45563_) + { + return super.clipWithInteractionOverride(p_45559_, p_45560_, p_45561_, p_45562_, p_45563_); + } + + @Override + public double getBlockFloorHeight(BlockPos p_45574_) + { + return super.getBlockFloorHeight(p_45574_); + } + + @Override + public double getBlockFloorHeight(VoxelShape p_45565_, Supplier p_45566_) + { + return super.getBlockFloorHeight(p_45565_, p_45566_); + } + + @Override + public Stream getBlockStates(AABB p_45557_) + { + return super.getBlockStates(p_45557_); + } + + @Override + public int getLightEmission(BlockPos p_45572_) + { + return super.getLightEmission(p_45572_); + } + + @Override + public int getMaxLightLevel() + { + return super.getMaxLightLevel(); + } + + @Override + public BlockHitResult isBlockInLine(ClipBlockStateContext p_151354_) + { + return super.isBlockInLine(p_151354_); + } + + @Override + public int getMaxBuildHeight() + { + return super.getMaxBuildHeight(); + } + + @Override + public int getMaxSection() + { + return super.getMaxSection(); + } + + @Override + public int getMinSection() + { + return super.getMinSection(); + } + + @Override + public int getSectionIndex(int p_151565_) + { + return super.getSectionIndex(p_151565_); + } + + @Override + public int getSectionIndexFromSectionY(int p_151567_) + { + return super.getSectionIndexFromSectionY(p_151567_); + } + + @Override + public int getSectionYFromSectionIndex(int p_151569_) + { + return super.getSectionYFromSectionIndex(p_151569_); + } + + @Override + public int getSectionsCount() + { + return super.getSectionsCount(); + } + + @Override + public boolean isOutsideBuildHeight(BlockPos p_151571_) + { + return super.isOutsideBuildHeight(p_151571_); + } + + @Override + public boolean isOutsideBuildHeight(int p_151563_) + { + return super.isOutsideBuildHeight(p_151563_); + } + + @Override + public @Nullable ModelDataManager getModelDataManager() + { + return super.getModelDataManager(); + } + + @Override + public float getShade(float normalX, float normalY, float normalZ, boolean shade) + { + return super.getShade(normalX, normalY, normalZ, shade); + } + + @Override + public boolean collidesWithSuffocatingBlock(@javax.annotation.Nullable Entity p_186438_, AABB p_186439_) + { + return super.collidesWithSuffocatingBlock(p_186438_, p_186439_); + } + + @Override + public Optional findFreePosition(@javax.annotation.Nullable Entity p_151419_, + VoxelShape p_151420_, + Vec3 p_151421_, + double p_151422_, + double p_151423_, + double p_151424_) + { + return super.findFreePosition(p_151419_, p_151420_, p_151421_, p_151422_, p_151423_, p_151424_); + } + + @Override + public Optional findSupportingBlock(Entity p_286468_, AABB p_286792_) + { + return super.findSupportingBlock(p_286468_, p_286792_); + } + + @Override + public Iterable getBlockCollisions(@javax.annotation.Nullable Entity p_186435_, AABB p_186436_) + { + return super.getBlockCollisions(p_186435_, p_186436_); + } + + @Override + public Iterable getCollisions(@javax.annotation.Nullable Entity p_186432_, AABB p_186433_) + { + return super.getCollisions(p_186432_, p_186433_); + } + + @Override + public boolean isUnobstructed(Entity p_45785_) + { + return super.isUnobstructed(p_45785_); + } + + @Override + public boolean isUnobstructed(BlockState p_45753_, BlockPos p_45754_, CollisionContext p_45755_) + { + return super.isUnobstructed(p_45753_, p_45754_, p_45755_); + } + + @Override + public boolean noCollision(AABB p_45773_) + { + return super.noCollision(p_45773_); + } + + @Override + public boolean noCollision(Entity p_45787_) + { + return super.noCollision(p_45787_); + } + + @Override + public boolean noCollision(@javax.annotation.Nullable Entity p_45757_, AABB p_45758_) + { + return super.noCollision(p_45757_, p_45758_); + } + + @Override + public int getBestNeighborSignal(BlockPos p_277977_) + { + return super.getBestNeighborSignal(p_277977_); + } + + @Override + public int getControlInputSignal(BlockPos p_277757_, Direction p_278104_, boolean p_277707_) + { + return super.getControlInputSignal(p_277757_, p_278104_, p_277707_); + } + + @Override + public int getDirectSignal(BlockPos p_277954_, Direction p_277342_) + { + return super.getDirectSignal(p_277954_, p_277342_); + } + + @Override + public int getDirectSignalTo(BlockPos p_277959_) + { + return super.getDirectSignalTo(p_277959_); + } + + @Override + public int getSignal(BlockPos p_277961_, Direction p_277351_) + { + return super.getSignal(p_277961_, p_277351_); + } + + @Override + public boolean hasNeighborSignal(BlockPos p_277626_) + { + return super.hasNeighborSignal(p_277626_); + } + + @Override + public boolean hasSignal(BlockPos p_277371_, Direction p_277391_) + { + return super.hasSignal(p_277371_, p_277391_); + } + + @Override + public boolean addFreshEntity(Entity p_46964_) + { + return super.addFreshEntity(p_46964_); + } + + @Override + public boolean destroyBlock(BlockPos p_46962_, boolean p_46963_) + { + return super.destroyBlock(p_46962_, p_46963_); + } + + @Override + public boolean destroyBlock(BlockPos p_46954_, boolean p_46955_, @javax.annotation.Nullable Entity p_46956_) + { + return super.destroyBlock(p_46954_, p_46955_, p_46956_); + } + + @Override + public float getMoonBrightness() + { + return super.getMoonBrightness(); + } + + @Override + public int getMoonPhase() + { + return super.getMoonPhase(); + } + + @Override + public float getTimeOfDay(float p_46943_) + { + return super.getTimeOfDay(p_46943_); + } + + @Override + public Collection> getPartEntities() + { + return super.getPartEntities(); + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability cap) + { + return super.getCapability(cap); + } + */ +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java new file mode 100644 index 0000000000..6c40a13aa0 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java @@ -0,0 +1,173 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeResolver; +import net.minecraft.world.level.biome.Climate.Sampler; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.chunk.PalettedContainerRO; +import net.minecraft.world.level.material.FluidState; +import java.util.function.Predicate; + +/** + * Porting: class is relatively small, just check super class manually (all of missing methods are/were just aliases) + */ +public class FakeLevelChunkSection extends LevelChunkSection +{ + private static final int SECTION_WIDTH = 16; + private static final int SECTION_HEIGHT = 16; + + private final FakeChunk fakeChunk; + private final int yIdx; + + /** + * @param fakeChunk parent chunk + * @param yIdx yLevel in chunk, multiply by section height + */ + public FakeLevelChunkSection(final FakeChunk fakeChunk, final int yIdx) + { + super(null, null); + this.fakeChunk = fakeChunk; + this.yIdx = yIdx; + + // set itself to cache + fakeChunk.lastY = yIdx; + fakeChunk.lastSection = this; + } + + private BlockPos formGlobalPos(int x, int y, int z) + { + return new BlockPos(x + fakeChunk.getPos().x() * SECTION_WIDTH, y + yIdx * SECTION_HEIGHT, z + fakeChunk.getPos().z() * SECTION_WIDTH); + } + + @Override + public BlockState setBlockState(int x, int y, int z, BlockState p_62995_, boolean p_62996_) + { + // this should return old value, but we don't allow changes + return getBlockState(x, y, z); + } + + @Override + public BlockState getBlockState(int x, int y, int z) + { + return fakeChunk.getBlockState(formGlobalPos(x, y, z)); + } + + @Override + public FluidState getFluidState(int x, int y, int z) + { + return fakeChunk.getFluidState(formGlobalPos(x, y, z)); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z) + { + return fakeChunk.getNoiseBiome(fakeChunk.getPos().x(), yIdx * SECTION_HEIGHT, fakeChunk.getPos().z()); + } + + @Override + public PalettedContainerRO> getBiomes() + { + // TODO: need our own? use clientLevel? + return null; + } + + @Override + public PalettedContainer getStates() + { + // TODO: need our own + return null; + } + + @Override + public boolean hasOnlyAir() + { + return false; + } + + @Override + public boolean isRandomlyTicking() + { + return false; + } + + @Override + public boolean isRandomlyTickingBlocks() + { + return false; + } + + @Override + public boolean isRandomlyTickingFluids() + { + return false; + } + + @Override + public boolean maybeHas(Predicate p_63003_) + { + // hard to say, so maybe yes + return true; + } + + @Override + public int getSerializedSize() + { + // technically noop, vanilla uses it for network sync + return 0; + } + + @Override + public void read(FriendlyByteBuf p_63005_) + { + // Noop + } + + @Override + public void readBiomes(FriendlyByteBuf p_275669_) + { + // Noop + } + + @Override + public void recalcBlockCounts() + { + // Noop + } + + @Override + public void release() + { + // Noop + } + + @Override + public void acquire() + { + // Noop + } + + @Override + public void fillBiomesFromNoise(BiomeResolver p_282075_, Sampler p_283084_, int p_282310_, int p_281510_, int p_283057_) + { + // Noop + } + + @Override + public void write(FriendlyByteBuf p_63012_) + { + // Noop + } + + /* + @Override + public BlockState setBlockState(int p_62987_, int p_62988_, int p_62989_, BlockState p_62990_) + { + return super.setBlockState(p_62987_, p_62988_, p_62989_, p_62990_); + } + */ +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java new file mode 100644 index 0000000000..ae320daf54 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java @@ -0,0 +1,72 @@ +package com.ldtteam.structurize.client.fakelevel; + +import java.util.function.Supplier; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.gamerules.GameRules; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.WritableLevelData; + +/** + * Minimal writable level data for client-only schematic preview levels. + */ +public class FakeLevelData implements WritableLevelData +{ + protected Supplier vanillaLevel; + protected final IFakeLevelLightProvider lightProvider; + + protected FakeLevelData(final Supplier vanillaLevel, final IFakeLevelLightProvider lightProvider) + { + this.vanillaLevel = vanillaLevel; + this.lightProvider = lightProvider; + } + + @Override + public LevelData.RespawnData getRespawnData() + { + return this.vanillaLevel.get().getLevelData().getRespawnData(); + } + + @Override + public void setSpawn(final LevelData.RespawnData respawnData) + { + // Spawn state is irrelevant to an in-memory preview level. + } + + @Override + public long getGameTime() + { + return this.vanillaLevel.get().getGameTime(); + } + + public long getDayTime() + { + return this.lightProvider.forceOwnLightLevel() + ? this.lightProvider.getDayTime() + : this.vanillaLevel.get().getOverworldClockTime(); + } + + public GameRules getGameRules() + { + return new GameRules(this.vanillaLevel.get().enabledFeatures()); + } + + @Override + public boolean isHardcore() + { + return false; + } + + @Override + public Difficulty getDifficulty() + { + // Keep entities alive if a preview is accidentally ticked. + return Difficulty.EASY; + } + + @Override + public boolean isDifficultyLocked() + { + return true; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelEntityGetterAdapter.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelEntityGetterAdapter.java new file mode 100644 index 0000000000..6cbddfe5e8 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelEntityGetterAdapter.java @@ -0,0 +1,53 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.util.AbortableIterationConsumer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.entity.EntityLookup; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.entity.LevelEntityGetterAdapter; +import net.minecraft.world.phys.AABB; +import java.util.Collection; +import java.util.Collections; +import java.util.function.Consumer; + +/** + * Vanilla equivalent without section storage. Porting: should override any usage of super.sectionStorage + */ +public class FakeLevelEntityGetterAdapter extends LevelEntityGetterAdapter +{ + public static final FakeLevelEntityGetterAdapter EMPTY = ofEntities(Collections.emptyList()); + protected static final EntityTypeTest ALWAYS_PASS_TEST = EntityTypeTest.forClass(Entity.class); + + protected FakeLevelEntityGetterAdapter(final EntityLookup entityLookup) + { + super(entityLookup, null); + } + + public static FakeLevelEntityGetterAdapter ofEntities(final Collection entities) + { + final EntityLookup entityLookup = new EntityLookup<>(); + entities.forEach(entityLookup::add); + return new FakeLevelEntityGetterAdapter(entityLookup); + } + + @Override + public void get(final AABB aabb, final Consumer sink) + { + get(ALWAYS_PASS_TEST, aabb, AbortableIterationConsumer.forConsumer(sink)); + } + + @Override + public void get(final EntityTypeTest predicate, + final AABB aabb, + final AbortableIterationConsumer sink) + { + for (final Entity e : getAll()) + { + final U entity = predicate.tryCast(e); + if (entity != null && entity.getBoundingBox().intersects(aabb) && sink.accept(entity).shouldAbort()) + { + return; + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelLightEngine.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelLightEngine.java new file mode 100644 index 0000000000..3a3e8d7b6e --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelLightEngine.java @@ -0,0 +1,226 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.chunk.DataLayer; +import net.minecraft.world.level.chunk.LightChunk; +import net.minecraft.world.level.chunk.LightChunkGetter; +import net.minecraft.world.level.lighting.LayerLightEventListener; +import net.minecraft.world.level.lighting.LayerLightSectionStorage.SectionType; +import net.minecraft.world.level.lighting.LevelLightEngine; +import javax.annotation.Nullable; + +/** + * Porting: class is relatively small, just check super class manually (all of missing methods are/were just aliases) + */ +public class FakeLevelLightEngine extends LevelLightEngine +{ + private final FakeLevel fakeLevel; + private FakeLevelLayerLightEventListener blockLightLayer = null; + private FakeLevelLayerLightEventListener skyLightLayer = null; + + public FakeLevelLightEngine(final FakeLevel level) + { + super(new LightChunkGetter() + { + @Override + public LightChunk getChunkForLighting(final int p_63023_, final int p_63024_) + { + throw new UnsupportedOperationException("Should never happen - FakeLevel light engine ctor"); + } + + @Override + public BlockGetter getLevel() + { + return level; + } + }, false, false); + + this.fakeLevel = level; + } + + @Override + public int getRawBrightness(final BlockPos p_75832_, final int p_75833_) + { + return fakeLevel.getRawBrightness(p_75832_, p_75833_); + } + + @Override + public String getDebugData(final LightLayer p_75817_, final SectionPos p_75818_) + { + return "FakeLevel light engine redirect - " + p_75817_; + } + + @Override + public LayerLightEventListener getLayerListener(final LightLayer p_75815_) + { + return switch (p_75815_) + { + case BLOCK -> { + if (blockLightLayer == null) + { + blockLightLayer = new FakeLevelLayerLightEventListener(p_75815_); + } + yield blockLightLayer; + } + case SKY -> { + if (skyLightLayer == null) + { + skyLightLayer = new FakeLevelLayerLightEventListener(p_75815_); + } + yield skyLightLayer; + } + }; + } + + @Override + public SectionType getDebugSectionType(final LightLayer p_285008_, final SectionPos p_285336_) + { + // Noop, only debug rendering ? + return SectionType.EMPTY; + } + + @Override + public void checkBlock(final BlockPos p_75823_) + { + // Noop + } + + @Override + public boolean hasLightWork() + { + // Noop + return false; + } + + @Override + public void propagateLightSources(final ChunkPos p_284998_) + { + // Noop + } + + @Override + public void queueSectionData(final LightLayer p_285328_, final SectionPos p_284962_, final DataLayer p_285035_) + { + // Noop + } + + @Override + public void retainData(final ChunkPos p_75829_, final boolean p_75830_) + { + // Noop + } + + @Override + public int runLightUpdates() + { + // Noop + return 0; + } + + @Override + public void setLightEnabled(final ChunkPos p_285439_, final boolean p_285012_) + { + // Noop + } + + @Override + public void updateSectionStatus(final SectionPos p_75827_, final boolean p_75828_) + { + // Noop + } + + @Override + public void updateSectionStatus(final BlockPos p_75835_, final boolean p_75836_) + { + // Noop + } + + /* + @Override + public int getLightSectionCount() + { + // super is fine + return super.getLightSectionCount(); + } + + @Override + public int getMaxLightSection() + { + // super is fine + return super.getMaxLightSection(); + } + + @Override + public int getMinLightSection() + { + // super is fine + return super.getMinLightSection(); + } + */ + + private class FakeLevelLayerLightEventListener implements LayerLightEventListener + { + private final LightLayer lightLayer; + + private FakeLevelLayerLightEventListener(final LightLayer lightLayer) + { + this.lightLayer = lightLayer; + } + + @Override + public void checkBlock(final BlockPos p_164454_) + { + // Noop + } + + @Override + public boolean hasLightWork() + { + // Noop + return false; + } + + @Override + public int runLightUpdates() + { + // Noop + return 0; + } + + @Override + public void updateSectionStatus(final SectionPos p_75837_, final boolean p_75838_) + { + // Noop + } + + @Override + public void setLightEnabled(final ChunkPos p_164452_, final boolean p_164453_) + { + // Noop + } + + @Override + public void propagateLightSources(final ChunkPos p_285263_) + { + // Noop + } + + @Override + @Nullable + public DataLayer getDataLayerData(final SectionPos p_75709_) + { + // Noop + return null; + } + + @Override + public int getLightValue(final BlockPos p_75710_) + { + return fakeLevel.getBrightness(lightLayer, p_75710_); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelBlockGetter.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelBlockGetter.java new file mode 100644 index 0000000000..4ce6a1e2f9 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelBlockGetter.java @@ -0,0 +1,162 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.CrashReportCategory; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.AABB; +import org.jetbrains.annotations.Nullable; +import java.util.function.Function; + +/** + * Data source for {@link FakeLevel} + */ +public interface IFakeLevelBlockGetter extends BlockGetter +{ + // TODO: change getSizeX/Z to int on next major + /** + * @return width for X axis + */ + short getSizeX(); + + /** + * @return width for Z axis + */ + short getSizeZ(); + + /** + * @return height for Y axis + */ + short getSizeY(); + + @Override + default int getMinY() + { + return 0; + } + + @Override + default int getHeight() + { + return getSizeY(); + } + + /** + * @return max Y coord exclusive + */ + default int getMaxYExclusive() + { + return getMinY() + getHeight(); + } + + /** + * @return min X coord inclusive + * @see #getMinBuildHeight() equivalent + */ + default int getMinX() + { + return 0; + } + + default int getMinBuildHeight() + { + return 0; + } + + /** + * @return min Z coord inclusive + * @see #getMinBuildHeight() equivalent + */ + default int getMinZ() + { + return 0; + } + + /** + * @return max X coord exclusive + * @see #getMaxYExclusive() equivalent + */ + default int getMaxX() + { + return getMinX() + getSizeX(); + } + + /** + * @return max Y coord exclusive + * @see #getMaxYExclusive() equivalent + */ + default int getMaxY() + { + return getMaxYExclusive(); + } + + /** + * @return max Z coord exclusive + * @see #getMaxYExclusive() equivalent + */ + default int getMaxZ() + { + return getMinZ() + getSizeZ(); + } + + /** + * @param pos tested pos + * @return true if inside aabb + * @see #isOutsideBuildHeight(BlockPos) extension of + */ + default boolean isPosInside(final BlockPos pos) + { + return getMinX() <= pos.getX() && pos.getX() < getMaxX() && + getMinBuildHeight() <= pos.getY() && pos.getY() < getMaxYExclusive() && + getMinZ() <= pos.getZ() && pos.getZ() < getMaxZ(); + } + + /** + * @param pos tested pos + * @return true if outside aabb + * @see #isOutsideBuildHeight(BlockPos) extension of + */ + default boolean isPosOutside(final BlockPos pos) + { + return !isPosInside(pos); + } + + @Override + default FluidState getFluidState(final BlockPos pos) + { + return isPosInside(pos) ? getBlockState(pos).getFluidState() : Fluids.EMPTY.defaultFluidState(); + } + + /** + * To show who is this fake level in level crashes + */ + default void describeSelfInCrashReport(final CrashReportCategory category) + {} + + /** + * @return null if pos is outside of aabb + */ + default BlockState getRawBlockState(final BlockPos pos) + { + return isPosInside(pos) ? getBlockState(pos) : null; + } + + /** + * @return function useful temporary insert into existing world + * @see #getRawBlockState(BlockPos) + */ + default Function getRawBlockStateFunction() + { + return this::getRawBlockState; + } + + /** + * @return aabb with end being blockpos-wise exclusive + */ + default AABB getAABB() + { + return new AABB(getMinX(), getMinBuildHeight(), getMinZ(), getMaxX(), getMaxYExclusive(), getMaxZ()); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelLightProvider.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelLightProvider.java new file mode 100644 index 0000000000..33b39f4a42 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelLightProvider.java @@ -0,0 +1,117 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.core.BlockPos; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.lighting.LightEngine; +import net.neoforged.neoforge.common.ModConfigSpec.IntValue; + +/** + * Loosely based on {@link BlockAndTintGetter} + */ +public interface IFakeLevelLightProvider +{ + public static final IFakeLevelLightProvider USE_CLIENT_LEVEL = new IFakeLevelLightProvider() + { + @Override + public boolean forceOwnLightLevel() + { + return false; + } + + @Override + public int getBlockLight(final BlockPos pos) + { + throw new UnsupportedOperationException("Noop light provider"); + } + + @Override + public int getSkyDarken() + { + throw new UnsupportedOperationException("Noop light provider"); + } + }; + + /** + * Returning false here means no other method from this iface will get called and all logic will be redirected to current client level. + * + * @return false if client level should be used instead + */ + boolean forceOwnLightLevel(); + + /** + * @return 0-15 lighting level for given pos + */ + int getBlockLight(BlockPos pos); + + /** + * @return sth sth vanilla daylight progress? + */ + int getSkyDarken(); + + /** + * @return day time from 0 to 24000 + */ + default long getDayTime() + { + return 6000; // noon + } + + /** + * @return 0-15 lighting level for given pos + */ + default int getSkyLight(final BlockPos pos) + { + return getBlockLight(pos); + } + + /** + * @return 0-15 lighting level for given pos + */ + default int getBrightness(final LightLayer lightLayer, final BlockPos pos) + { + return lightLayer == LightLayer.SKY ? getSkyLight(pos) : getBlockLight(pos); + } + + /** + * @return 0-15 lighting level for given pos + */ + default int getRawBrightness(final BlockPos pos, final int skyAmount) + { + final int sky = getSkyLight(pos) - skyAmount; + final int block = getBlockLight(pos); + return Math.max(block, sky); + } + + /** + * Simple light level config + */ + public static class ConfigBasedLightProvider implements IFakeLevelLightProvider + { + private final IntValue configValue; + + public ConfigBasedLightProvider(final IntValue configValue) + { + this.configValue = configValue; + } + + @Override + public boolean forceOwnLightLevel() + { + final int val = configValue.get(); + return 0 <= val && val <= LightEngine.MAX_LEVEL; + } + + @Override + public int getBlockLight(final BlockPos pos) + { + return configValue.get(); + } + + @Override + public int getSkyDarken() + { + return configValue.get(); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java new file mode 100644 index 0000000000..0ea8c6b361 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java @@ -0,0 +1,189 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.CrashReportCategory; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.core.registries.BuiltInRegistries; +import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Simple implementation of {@link IFakeLevelBlockGetter} mostly for usage in methods where {@link Level} is needed for virtual + * BE/entities etc. + */ +public class SingleBlockFakeLevel extends FakeLevel +{ + /** + * Creates simple fakeLevel instance + * + * @param realLevel actual valid vanilla instance to provide eg. registries + */ + public SingleBlockFakeLevel(final Level realLevel) + { + super(new SingleBlockFakeLevelGetter(), IFakeLevelLightProvider.USE_CLIENT_LEVEL, null, true); + super.setRealLevel(realLevel); + } + + @Override + public SingleBlockFakeLevelGetter getLevelSource() + { + return (SingleBlockFakeLevelGetter) super.getLevelSource(); + } + + /** + * Do not forget to unset to prevent potential memory leaks + * + * @param blockState related to blockEntity + * @param blockEntity related to blockState + * @param realLevel actual valid vanilla instance to provide eg. registries + * @see #unset(BlockEntity) + * @see FakeLevel#setEntities(Collection) FakeLevel#setEntities(Collection) if you want to add entities, do not forget to reset + */ + public void prepare(final BlockState blockState, @Nullable final BlockEntity blockEntity, final Level realLevel) + { + getLevelSource().blockEntity = blockEntity; + getLevelSource().blockState = blockState; + setRealLevel(realLevel); + + if (blockEntity != null) + { + blockEntity.setLevel(this); + } + } + + /** + * @param blockEntity to unlink level if needed + * @see #prepare(BlockState, BlockEntity, Level) + */ + public void unset(@Nullable final BlockEntity blockEntity) + { + getLevelSource().blockEntity = null; + getLevelSource().blockState = null; + setRealLevel(null); + + if (blockEntity != null) + { + try + { + blockEntity.setLevel(null); + } + catch (final NullPointerException e) + { + // setLevel impls sometimes violates nullability of level field + } + } + } + + /** + * See related methods for more information. + * + * @param blockState related to blockEntity + * @param blockEntity related to blockState + * @param realLevel actual valid vanilla instance to provide eg. registries + * @param action context action + * @see #prepare(BlockState, BlockEntity, Level) + * @see #unset(BlockEntity) + */ + public void withFakeLevelContext(final BlockState blockState, + @Nullable final BlockEntity blockEntity, + final Level realLevel, + final Consumer action) + { + prepare(blockState, blockEntity, realLevel); + action.accept(this); + unset(blockEntity); + } + + /** + * See related methods for more information. + * + * @param blockState related to blockEntity + * @param blockEntity related to blockState + * @param realLevel actual valid vanilla instance to provide eg. registries + * @param action context action + * @see #prepare(BlockState, BlockEntity, Level) + * @see #unset(BlockEntity) + */ + public T useFakeLevelContext(final BlockState blockState, + @Nullable final BlockEntity blockEntity, + final Level realLevel, + final Function action) + { + prepare(blockState, blockEntity, realLevel); + final T result = action.apply(this); + unset(blockEntity); + return result; + } + + public static class SingleBlockFakeLevelGetter implements IFakeLevelBlockGetter + { + public BlockState blockState = null; + public BlockEntity blockEntity = null; + + @Override + public BlockEntity getBlockEntity(final BlockPos pos) + { + return blockEntity; + } + + @Override + public BlockState getBlockState(final BlockPos pos) + { + return blockState; + } + + @Override + public int getHeight() + { + return 1; + } + + @Override + public short getSizeX() + { + return 1; + } + + @Override + public short getSizeZ() + { + return 1; + } + + @Override + public short getSizeY() + { + return 1; + } + + @Override + public void describeSelfInCrashReport(final CrashReportCategory category) + { + category.setDetail("Single block", blockState::toString); + category.setDetail("Single block entity type", + () -> blockEntity == null ? null : BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntity.getType()).toString()); + } + } + + public static class SidedSingleBlockFakeLevel + { + private SingleBlockFakeLevel client; + private SingleBlockFakeLevel server; + + public SingleBlockFakeLevel get(final Level realLevel) + { + if (realLevel.isClientSide()) + { + return client != null ? client : (client = new SingleBlockFakeLevel(realLevel)); + } + else + { + return server != null ? server : (server = new SingleBlockFakeLevel(realLevel)); + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java b/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java index 6c61ff0531..9f35347a32 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java @@ -1,21 +1,20 @@ package com.ldtteam.structurize.client.gui; import com.google.gson.internal.LazilyParsedNumber; +import com.ldtteam.blockui.Alignment; +import com.ldtteam.blockui.Color; import com.ldtteam.blockui.Pane; import com.ldtteam.blockui.PaneBuilders; -import com.ldtteam.blockui.controls.ButtonImage; -import com.ldtteam.blockui.controls.Image; -import com.ldtteam.blockui.controls.Text; -import com.ldtteam.blockui.controls.TextFieldVanilla; -import com.ldtteam.blockui.views.BOWindow; +import com.ldtteam.blockui.controls.*; import com.ldtteam.blockui.views.ScrollingList; +import com.ldtteam.blockui.views.View; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.TranslationConstants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintTagUtils; -import com.ldtteam.structurize.client.BlueprintHandler; import com.ldtteam.structurize.client.ModKeyMappings; +import com.ldtteam.structurize.config.AbstractConfiguration; import com.ldtteam.structurize.network.messages.BuildToolPlacementMessage; import com.ldtteam.structurize.storage.ISurvivalBlueprintHandler; import com.ldtteam.structurize.storage.SurvivalBlueprintHandlers; @@ -24,10 +23,12 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.KeyEvent; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.contents.TranslatableContents; -import net.minecraft.util.Tuple; +import net.minecraft.resources.Identifier; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; @@ -38,10 +39,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; -import static com.ldtteam.structurize.api.constants.Constants.*; -import static com.ldtteam.structurize.api.constants.GUIConstants.*; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; +import static com.ldtteam.structurize.api.util.constant.GUIConstants.*; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; import static com.ldtteam.structurize.client.gui.util.InputFilters.ONLY_NUMBERS; /** @@ -115,7 +117,6 @@ public AbstractBlueprintManipulationWindow(@NotNull final String resourceId, @Nu registerButton(BUTTON_ROTATE_RIGHT, this::rotateRightClicked); registerButton(BUTTON_ROTATE_LEFT, this::rotateLeftClicked); registerButton(BUTTON_SETTINGS, this::settingsClicked); - registerButton(BUTTON_CONTENTS, this::openContents); settingsList = findPaneOfTypeByID("settinglist", ScrollingList.class); placementOptionsList = findPaneOfTypeByID("placement", ScrollingList.class); @@ -133,7 +134,7 @@ public void onOpened() if (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getPos() == null) { Utils.playErrorSound(Minecraft.getInstance().player); - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.missing.pos"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.missing.pos")); cancelClicked(); } } @@ -153,13 +154,13 @@ protected void confirmClicked() { if (!Minecraft.getInstance().player.isCreative()) { - final List handlers = SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getRotationMirror()); + final List handlers = SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getPlacementSettings()); if (handlers.isEmpty()) { Utils.playErrorSound(Minecraft.getInstance().player); if (SurvivalBlueprintHandlers.getHandlers().isEmpty()) { - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.no.survival.handler"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.no.survival.handler")); } return; } @@ -220,7 +221,7 @@ public void updatePlacementOptions() final BlueprintPreviewData previewData = RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId); if (previewData.getBlueprint() != null) { - for (final ISurvivalBlueprintHandler handler : SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getRotationMirror())) + for (final ISurvivalBlueprintHandler handler : SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getPlacementSettings())) { categories.add(new Tuple<>(handler.getDisplayName(), () -> handlePlacement(BuildToolPlacementMessage.HandlerType.Survival, handler.getId()))); } @@ -243,12 +244,13 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { final ButtonImage buttonImage = rowPane.findPaneOfTypeByID("type", ButtonImage.class); buttonImage.setText(categories.get(index).getA()); - buttonImage.setTextColor(ChatFormatting.BLACK.getColor()); + buttonImage.setTextColor(0xFF000000); buttonImage.setHandler(button -> categories.get(index).getB().run()); } }); @@ -262,15 +264,14 @@ public void onUpdate() { findPaneOfTypeByID("tip", Text.class).setVisible(false); } - findPaneByID(BUTTON_CONTENTS).setVisible(RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getBlueprint() != null); } @Override - public boolean onUnhandledKeyTyped(final int ch, final int key) + public boolean onUnhandledKeyTyped(final KeyEvent event) { - if (ch != 0 || getFocus() != null) return super.onUnhandledKeyTyped(ch, key); + if (getFocus() != null) return super.onUnhandledKeyTyped(event); - final InputConstants.Key inputKey = InputConstants.Type.KEYSYM.getOrCreate(key); + final InputConstants.Key inputKey = InputConstants.Type.KEYSYM.getOrCreate(event.key()); if (ModKeyMappings.MOVE_FORWARD.get().isActiveAndMatches(inputKey)) { @@ -314,7 +315,7 @@ else if (ModKeyMappings.PLACE.get().isActiveAndMatches(inputKey)) } else { - return super.onUnhandledKeyTyped(ch, key); + return super.onUnhandledKeyTyped(event); } return true; } @@ -341,9 +342,17 @@ protected void initSettings() settingsList.setDataProvider(settings::size, (index, rowPane) -> { final ConfigValue setting = settings.get(index); - final ValueSpec settingSpec = setting.getSpec(); + final Optional optional = Structurize.getConfig().getSpecFromValue(setting); final Text label = rowPane.findPaneOfTypeByID("label", Text.class); + if (optional.isEmpty()) + { + // config resolution failed (crashes in dev already, display error in production) + label.setText(Component.translatable(TranslationConstants.NO_VALUE_SPEC)); + return; + } + + final ValueSpec settingSpec = optional.get(); final String nameTKey = settingSpec.getTranslationKey(); if (label.getText() != null && label.getText().getContents() instanceof final TranslatableContents tkey && tkey.getKey().equals(nameTKey)) @@ -353,7 +362,7 @@ protected void initSettings() } label.setText(Component.translatable(nameTKey)); - PaneBuilders.singleLineTooltip(Component.literal(settingSpec.getComment()), rowPane); + PaneBuilders.singleLineTooltip(Component.translatable(nameTKey + AbstractConfiguration.COMMENT_SUFFIX), rowPane); final ButtonImage buttonImage = rowPane.findPaneOfTypeByID("switch", ButtonImage.class); final TextFieldVanilla inputField = rowPane.findPaneOfTypeByID("set_input", TextFieldVanilla.class); @@ -373,7 +382,7 @@ protected void initSettings() buttonImage.setText(Component.translatable(newValue ? "options.on" : "options.off")); }); } - else if (setting.get() instanceof Number) + else if (setting.get() instanceof final Number value) { final ConfigValue typedSetting = (ConfigValue) setting; @@ -407,24 +416,67 @@ else if (setting.get() instanceof Number) if (setting == rendererTransparency && rendererTransparency.get() < 0) { // TODO: move to standalone ui - final BOWindow confirmDialog = new BOWindow(Constants.resLocStruct("gui/dialogconfirmtransparency.xml")); - - confirmDialog.findPaneOfTypeByID("confirm", ButtonImage.class).setHandler(b -> { + final View confirmDialog = new View(); + confirmDialog.setPosition(70, 0); + confirmDialog.setSize(177, 150); + confirmDialog.setAlignment(Alignment.MIDDLE); + + final Gradient hidingLayer = new Gradient(); + hidingLayer.setGradientStart(0x10, 0x10, 0x10, 0xC0); + hidingLayer.setGradientEnd(0x10, 0x10, 0x10, 0xD0); + hidingLayer.setSize(getWindow().getWidth(), getWindow().getHeight()); + + getWindow().addChild(hidingLayer); + getWindow().addChild(confirmDialog); + View.setFocus(null); + + final Image background = new Image(); + background.setSize(177, 150); + background.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/builderhut/builder_papper.png"), false); + confirmDialog.addChild(background); + + final Text text = new Text(); + text.setPosition(10, 8); + text.setSize(157, 105); + text.setColors(Color.getByName("black")); + text.setTextAlignment(Alignment.TOP_MIDDLE); + text.setText(Component.translatable("structurize.config.transparency.warning")); + confirmDialog.addChild(text); + + final ButtonImage confirm = new ButtonImage(); + confirm.setPosition(10, 123); + confirm.setSize(64, 17); + confirm.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/builderhut/builder_button_small.png")); + confirm.setColors(Color.getByName("black")); + confirm.setTextAlignment(Alignment.MIDDLE); + confirm.setTextRenderBox(64, 17); + confirm.setText(Component.translatable("gui.yes")); + confirm.setHandler(b -> { final double newVal = newValue.doubleValue(); Structurize.getConfig().set(rendererTransparency, newVal < 0 ? 1 : newVal); if (newVal < 0) { inputField.setText("1.0"); } - confirmDialog.close(); + getWindow().removeChild(hidingLayer); + getWindow().removeChild(confirmDialog); }); + confirmDialog.addChild(confirm); - confirmDialog.findPaneOfTypeByID("cancel", ButtonImage.class).setHandler(b -> { + final ButtonImage cancel = new ButtonImage(); + cancel.setPosition(103, 123); + cancel.setSize(64, 17); + cancel.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/builderhut/builder_button_small.png")); + cancel.setColors(Color.getByName("black")); + cancel.setTextAlignment(Alignment.MIDDLE); + cancel.setTextRenderBox(64, 17); + cancel.setText(Component.translatable("gui.cancel")); + cancel.setHandler(b -> { inputField.setText(Double.toString(rendererTransparency.get())); - confirmDialog.close(); + getWindow().removeChild(hidingLayer); + getWindow().removeChild(confirmDialog); }); - - confirmDialog.openAsLayer(); + confirmDialog.addChild(cancel); } else { @@ -533,12 +585,6 @@ private void rotateLeftClicked() updateRotationState(); } - private void openContents() - { - final BlueprintPreviewData previewData = RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId); - new WindowBlockGetterContents(previewData.getBlueprint(), Minecraft.getInstance().level, BlueprintHandler.getInstance().getOptionalEntitiesForBlueprint(previewData)).openAsLayer(); - } - /* * ---------------- Miscellaneous ---------------- */ @@ -548,7 +594,7 @@ private void openContents() */ protected void updateRotationState() { - findPaneOfTypeByID(BUTTON_MIRROR, ButtonImage.class).setImage(Constants.resLocStruct(String.format(RES_STRING, BUTTON_MIRROR + (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getRotationMirror().mirror().equals(Mirror.NONE) ? "" : GREEN_POS)))); + findPaneOfTypeByID(BUTTON_MIRROR, ButtonImage.class).setImage(Identifier.fromNamespaceAndPath(MOD_ID, String.format(RES_STRING, BUTTON_MIRROR + (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getRotationMirror().mirror().equals(Mirror.NONE) ? "" : GREEN_POS)))); final String rotation = switch (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getRotationMirror().rotation()) { @@ -557,7 +603,7 @@ protected void updateRotationState() case COUNTERCLOCKWISE_90 -> "left_green"; case NONE -> "up_green"; }; - findPaneOfTypeByID(IMAGE_ROTATION, Image.class).setImage(Constants.resLocStruct(String.format(RES_STRING, rotation)), false); + findPaneOfTypeByID(IMAGE_ROTATION, Image.class).setImage(Identifier.fromNamespaceAndPath(MOD_ID, String.format(RES_STRING, rotation)), false); } /** diff --git a/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java b/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java index c7dc366abf..a757ede756 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java @@ -3,8 +3,8 @@ import com.ldtteam.blockui.controls.Button; import com.ldtteam.blockui.controls.ButtonHandler; import com.ldtteam.blockui.views.BOWindow; -import com.ldtteam.structurize.api.Log; -import net.minecraft.resources.ResourceLocation; +import com.ldtteam.structurize.api.util.Log; +import net.minecraft.resources.Identifier; import java.util.HashMap; import java.util.function.Consumer; @@ -23,7 +23,7 @@ public abstract class AbstractWindowSkeleton extends BOWindow implements ButtonH */ public AbstractWindowSkeleton(final String resource) { - super(ResourceLocation.parse(resource)); + super(Identifier.parse(resource)); buttons = new HashMap<>(); } @@ -32,7 +32,7 @@ public AbstractWindowSkeleton(final String resource) * * @param resource Resource location */ - public AbstractWindowSkeleton(final ResourceLocation resource) + public AbstractWindowSkeleton(final Identifier resource) { super(resource); buttons = new HashMap<>(); diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowBlockGetterContents.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowBlockGetterContents.java deleted file mode 100644 index 44b2ebf3eb..0000000000 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowBlockGetterContents.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.ldtteam.structurize.client.gui; - -import com.ldtteam.blockui.Pane; -import com.ldtteam.blockui.controls.ItemIcon; -import com.ldtteam.blockui.controls.Text; -import com.ldtteam.blockui.views.BOWindow; -import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.common.util.BlockToItemHelper; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.ItemStorage; -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.blueprints.v1.Blueprint; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * Computes as exact as possible contents of given AABB. This should be used as base for item related analysis - */ -public class WindowBlockGetterContents extends BOWindow -{ - public WindowBlockGetterContents(final Blueprint blueprint, final Level realLevel, final Collection boundedEntities) - { - this(blueprint, - realLevel, - new BlockPos(blueprint.getMinX(), blueprint.getMinBuildHeight(), blueprint.getMinZ()), - new BlockPos(blueprint.getMaxX() - 1, blueprint.getMaxBuildHeight() - 1, blueprint.getMaxZ() - 1), - boundedEntities); - } - - /** - * @param blockGetter level - * @param realLevel vanilla level instance in case of blockGetter not being one - * @param start inclusive from - * @param end inclusive to - * @param boundedEntities entities bounded by [start, end] parameters - */ - public WindowBlockGetterContents(final BlockGetter blockGetter, - @Nullable final Level realLevel, - final BlockPos start, - final BlockPos end, - final Collection boundedEntities) - { - super(Constants.resLocStruct("gui/windowcontents.xml")); - - final Map blocks = new HashMap<>(); - final Map blockItemHandlers = new HashMap<>(); - final Map entities = new HashMap<>(); - final Map entityItemHandlers = new HashMap<>(); - - for (final BlockPos pos : BlockPos.betweenClosed(start, end)) - { - final BlockState blockState = blockGetter.getBlockState(pos); - final BlockEntity blockEntity = blockGetter.getBlockEntity(pos); - - // blockstate - addAmountToMap(blocks, BlockToItemHelper.getItemStack(blockState, blockEntity, Minecraft.getInstance().player)); - - // blockentity content - if (blockEntity != null && blockEntity.getLevel() == null) - { - ItemStackUtils.ITEM_HANDLER_FAKE_LEVEL.get(realLevel).withFakeLevelContext(blockState, blockEntity, realLevel, level -> { - ItemStackUtils.getItemHandlersFromProvider(blockEntity, pos, blockState) - .forEach(i -> ItemStackUtils.deepExtractItemHandler(i, stack -> addAmountToMap(blockItemHandlers, stack))); - }); - } - else - { - ItemStackUtils.getItemHandlersFromProvider(blockEntity, pos, blockState) - .forEach(i -> ItemStackUtils.deepExtractItemHandler(i, stack -> addAmountToMap(blockItemHandlers, stack))); - } - } - - for (final Entity entity : boundedEntities) - { - addAmountToMap(entities, ItemStackUtils.getEntitySpawningItem(entity)); - ItemStackUtils.getItemStacksOfEntity(entity).forEach(stack -> addAmountToMap(entityItemHandlers, stack)); - } - - final List blockList = new ArrayList<>(blocks.values()); - final List blockItemHandlerList = new ArrayList<>(blockItemHandlers.values()); - final List entityList = new ArrayList<>(entities.values()); - final List entityItemHandlerList = new ArrayList<>(entityItemHandlers.values()); - - final Comparator alphabeticalOrder = - (i1, i2) -> i1.getItemStack().getHoverName().getString().compareTo(i2.getItemStack().getHoverName().getString()); - blockList.sort(alphabeticalOrder); - blockItemHandlerList.sort(alphabeticalOrder); - entityList.sort(alphabeticalOrder); - entityItemHandlerList.sort(alphabeticalOrder); - - findPaneOfTypeByID("blocks", ScrollingList.class).setDataProvider(blockList::size, - (idx, pane) -> updateItem(blockList.get(idx), pane)); - findPaneOfTypeByID("block_item_handlers", ScrollingList.class).setDataProvider(blockItemHandlerList::size, - (idx, pane) -> updateItem(blockItemHandlerList.get(idx), pane)); - findPaneOfTypeByID("entities", ScrollingList.class).setDataProvider(entityList::size, - (idx, pane) -> updateItem(entityList.get(idx), pane)); - findPaneOfTypeByID("entity_item_handlers", ScrollingList.class).setDataProvider(entityItemHandlerList::size, - (idx, pane) -> updateItem(entityItemHandlerList.get(idx), pane)); - } - - private void addAmountToMap(final Map itemSet, @Nullable final ItemStack blockAsItem) - { - if (blockAsItem != null) - { - itemSet.computeIfAbsent(blockAsItem.getItem(), i -> new ItemStorage(blockAsItem.copyWithCount(1), 0, true, true)) - .addAmount(Math.max(1, blockAsItem.getCount())); - } - } - - private void updateItem(final ItemStorage itemStorage, final Pane pane) - { - pane.findPaneOfTypeByID("icon", ItemIcon.class).setItem(itemStorage.getItemStack()); - pane.findPaneOfTypeByID("registry_key", Text.class).setText(itemStorage.getItemStack().getHoverName()); - pane.findPaneOfTypeByID("amount", Text.class).setText(Component.literal(Integer.toString(itemStorage.getAmount()))); - } -} diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java index 90aa40c4d7..4f88b0dde6 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java @@ -8,8 +8,8 @@ import com.ldtteam.blockui.util.resloc.OutOfJarResourceLocation; import com.ldtteam.blockui.views.ScrollingList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blocks.interfaces.ILeveledBlueprintAnchorBlock; import com.ldtteam.structurize.blocks.interfaces.INamedBlueprintAnchorBlock; import com.ldtteam.structurize.blocks.interfaces.IRequirementsBlueprintAnchorBlock; @@ -24,10 +24,10 @@ import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.block.state.BlockState; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; @@ -39,11 +39,11 @@ import java.util.function.BiConsumer; import java.util.function.Predicate; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; -import static com.ldtteam.structurize.api.constants.GUIConstants.BUTTON_SWITCH_STYLE; -import static com.ldtteam.structurize.api.constants.GUIConstants.DEFAULT_ICON; -import static com.ldtteam.structurize.api.constants.WindowConstants.BUILD_TOOL_RESOURCE_SUFFIX; -import static com.ldtteam.structurize.api.constants.WindowConstants.BUTTON_CONFIRM; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.GUIConstants.BUTTON_SWITCH_STYLE; +import static com.ldtteam.structurize.api.util.constant.GUIConstants.DEFAULT_ICON; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.BUILD_TOOL_RESOURCE_SUFFIX; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.BUTTON_CONFIRM; /** * BuildTool window. @@ -146,8 +146,6 @@ public final class WindowExtendedBuildTool extends AbstractBlueprintManipulation */ private final Predicate availableBlueprintPredicate; - private final HolderLookup.Provider provider; - /** * Type of button. */ @@ -160,10 +158,9 @@ public enum ButtonType public WindowExtendedBuildTool( final BlockPos pos, - final int groundstyle, - final HolderLookup.Provider provider) + final int groundstyle) { - this(pos, groundstyle, null, BLOCK_BLUEPRINT_REQUIREMENT, provider); + this(pos, groundstyle, null, BLOCK_BLUEPRINT_REQUIREMENT); } /** @@ -181,13 +178,11 @@ public WindowExtendedBuildTool( final BlockPos pos, final int groundstyle, @Nullable final BiConsumer selectionCallback, - @Nullable final Predicate availableBlueprintPredicate, - final HolderLookup.Provider provider) + @Nullable final Predicate availableBlueprintPredicate) { super(MOD_ID + BUILD_TOOL_RESOURCE_SUFFIX, pos, groundstyle, "blueprint"); this.selectionCallback = selectionCallback; this.availableBlueprintPredicate = availableBlueprintPredicate; - this.provider = provider; this.init(groundstyle, pos); registerButton(BUTTON_SWITCH_STYLE, this::switchPackClicked); } @@ -288,8 +283,7 @@ private void switchPackClicked() new WindowSwitchPack(() -> new WindowExtendedBuildTool(RenderingCache.getOrCreateBlueprintPreviewData("blueprint").getPos(), groundstyle, selectionCallback, - availableBlueprintPredicate, - provider)).open(); + availableBlueprintPredicate)).open(); } @Override @@ -298,7 +292,7 @@ protected void cancelClicked() BlueprintPreviewData previewData = RenderingCache.removeBlueprint("blueprint"); previewData.setBlueprint(null); previewData.setPos(BlockPos.ZERO); - new SyncPreviewCacheToServer(previewData).sendToServer(); + Network.getNetwork().sendToServer(new SyncPreviewCacheToServer(previewData)); close(); @@ -327,16 +321,19 @@ protected void handlePlacement(final BuildToolPlacementMessage.HandlerType type, final BlueprintPreviewData previewData = RenderingCache.getOrCreateBlueprintPreviewData("blueprint"); if (previewData.getBlueprint() != null) { - new BuildToolPlacementMessage(type, + Network.getNetwork() + .sendToServer(new BuildToolPlacementMessage(type, id, currentStructurePack, StructurePacks.getStructurePack(currentStructurePack).getSubPath(previewData.getBlueprint().getFilePath().resolve(previewData.getBlueprint().getFileName() + ".blueprint")), previewData.getPos(), - previewData.getRotationMirror()).sendToServer(); - if (type == BuildToolPlacementMessage.HandlerType.Survival) - { - cancelClicked(); - } + previewData.getRotationMirror().rotation(), + previewData.getRotationMirror().mirror())); + // A placement request consumes the current preview regardless of + // whether it is a survival handler or an instant creative + // placement. Keeping the cache for Complete/Pretty leaves the + // just-constructed building covered by a stale ghost blueprint. + cancelClicked(); } } @@ -366,12 +363,12 @@ public void onUpdate() } catch (final Exception ex) { - img.setImage(Constants.resLocStruct(DEFAULT_ICON)); + img.setImage(Identifier.parse(DEFAULT_ICON)); } } else { - img.setImage(Constants.resLocStruct(DEFAULT_ICON)); + img.setImage(Identifier.parse(DEFAULT_ICON)); } final String id = category.subPath; @@ -384,7 +381,7 @@ public void onUpdate() if (category.isTerminal) { - blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id, provider)); + blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id)); } else { @@ -412,7 +409,7 @@ public void onUpdate() if (subCats.isEmpty()) { nextDepthMeta.remove(nextDepth); - blueprintsAtDepth.put(nextDepth, StructurePacks.getBlueprintsFuture(id, nextDepth, provider)); + blueprintsAtDepth.put(nextDepth, StructurePacks.getBlueprintsFuture(id, nextDepth)); } else { @@ -421,7 +418,7 @@ public void onUpdate() final String id = subCat.subPath; if (subCat.isTerminal) { - blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id, provider)); + blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id)); } else { @@ -548,6 +545,7 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { @@ -680,6 +678,7 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { @@ -728,13 +727,14 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { final ButtonImage button = rowPane.findPaneOfTypeByID("alternative", ButtonImage.class); rowPane.findPaneOfTypeByID("id", Text.class).setText(Component.literal(depth + ":" + list.get(index).getKey())); button.setText(Component.literal(list.get(index).getKey())); - button.setTextColor(ChatFormatting.BLACK.getColor()); + button.setTextColor(0xFF000000); } }); } @@ -775,25 +775,26 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { if (blueprints.get(index) == null) { - final String buttonId = depth.substring(0, depth.lastIndexOf(":")) + ":$back"; + final String buttonId = depth.substring(0, depth.lastIndexOf(":")) + ":back"; final ButtonImage button = rowPane.findPaneOfTypeByID("level", ButtonImage.class); rowPane.findPaneOfTypeByID("id", Text.class).setText(Component.literal(buttonId)); button.setText(Component.literal("")); - button.setImage(Constants.resLocStruct("textures/gui/buildtool/back_medium.png")); + button.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/back_medium.png")); } else { final String buttonId = depth + ":" + (hasAlternatives ? index - 1 : index); final ButtonImage button = rowPane.findPaneOfTypeByID("level", ButtonImage.class); rowPane.findPaneOfTypeByID("id", Text.class).setText(Component.literal(buttonId)); - button.setImage(Constants.resLocStruct("textures/gui/buildtool/button_medium.png")); + button.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_medium.png")); button.setText(Component.literal("Level: " + (index + (hasAlternatives ? 0 : 1)))); - button.setTextColor(ChatFormatting.BLACK.getColor()); + button.setTextColor(0xFF000000); } } }); @@ -806,11 +807,11 @@ private void handleBlueprint(final ButtonData buttonData, final Pane rowPane, fi { if (img == null) { - img = rowPane.findPaneOfTypeByID("$back:" + buttonData.data, ButtonImage.class); + img = rowPane.findPaneOfTypeByID("back:" + buttonData.data, ButtonImage.class); } - img.setID("$back:" + buttonData.data); + img.setID("back:" + buttonData.data); img.setVisible(true); - img.setImage(Constants.resLocStruct("textures/gui/buildtool/back_medium.png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/back_medium.png")); PaneBuilders.tooltipBuilder().hoverPane(img).build().setText(Component.literal("back")); } else if (buttonData.type == ButtonType.Blueprint) @@ -872,7 +873,7 @@ else if (buttonData.type == ButtonType.Blueprint) } PaneBuilders.tooltipBuilder().hoverPane(img).build().setText(toolTip); - img.setImage(Constants.resLocStruct("textures/gui/buildtool/button_blueprint_disabled" + (hasAlts ? "_variant" : "") + ".png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_blueprint_disabled" + (hasAlts ? "_variant" : "") + ".png")); isLocked = true; } @@ -880,11 +881,11 @@ else if (buttonData.type == ButtonType.Blueprint) if (isCurrentlySelected) { - img.setImage(Constants.resLocStruct("textures/gui/buildtool/button_blueprint_selected" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_blueprint_selected" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); } else if (!isLocked) { - img.setImage(Constants.resLocStruct("textures/gui/buildtool/button_blueprint" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_blueprint" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); } } } @@ -912,11 +913,11 @@ private void handleSubCat(final ButtonData buttonData, final Pane rowPane, final { if (img == null) { - img = rowPane.findPaneOfTypeByID("$back:" + buttonData.data, ButtonImage.class); + img = rowPane.findPaneOfTypeByID("back:" + buttonData.data, ButtonImage.class); } - img.setID("$back:" + buttonData.data); + img.setID("back:" + buttonData.data); img.setVisible(true); - img.setImage(Constants.resLocStruct("textures/gui/buildtool/back_medium.png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/back_medium.png")); PaneBuilders.tooltipBuilder().hoverPane(img).build().setText(Component.literal("back")); return; } @@ -935,14 +936,14 @@ private void handleSubCat(final ButtonData buttonData, final Pane rowPane, final final Component desc = Component.literal(descString); img.setText(desc); img.setVisible(true); - img.setTextColor(ChatFormatting.BLACK.getColor()); + img.setTextColor(0xFF000000); } @Override public void onButtonClicked(final Button button) { boolean handled = false; - if (button.getID().contains("$back:")) + if (button.getID().contains("back:")) { // When leaving the current folder, the alternatives should also disable then. alternativesList.hide(); @@ -1020,7 +1021,7 @@ else if (button.getID().contains(":")) pane.enable(); } - currentBlueprintCat = button.getID().replace(":$back", ""); + currentBlueprintCat = button.getID().replace(":back", ""); handleBlueprintCategory(currentBlueprintCat, false); button.setHoverPane(null); handled = true; @@ -1032,7 +1033,7 @@ else if (button.getID().equals("alternative") || button.getID().equals("level")) pane.enable(); } - currentBlueprintCat = button.getParent().findPaneOfTypeByID("id", Text.class).getText().getString().replace(":$back", ""); + currentBlueprintCat = button.getParent().findPaneOfTypeByID("id", Text.class).getText().getString().replace(":back", ""); handleBlueprintCategory(currentBlueprintCat, false); button.setHoverPane(null); handled = true; @@ -1145,6 +1146,11 @@ private void setBlueprint(Blueprint blueprint) adjustToGroundOffset(); selectedBlueprint = blueprint; + // Blueprint selection can happen from the async category/level lists, + // before the generic button-dispatch path runs. Keep the movement and + // rotation controls visible as soon as a preview is actually ready. + findPaneOfTypeByID("manipulator", View.class).setVisible(true); + final boolean canBuild = availableBlueprintPredicate == null || availableBlueprintPredicate.test(blueprint); findPaneOfTypeByID(BUTTON_CONFIRM, Button.class).setVisible(canBuild); @@ -1164,11 +1170,4 @@ public ButtonData(ButtonType type, Object data) this.data = data; } } - - public static void clearStaticData() - { - nextDepthMeta.clear(); - blueprintsAtDepth.clear(); - currentBluePrintMappingAtDepthCache.clear(); - } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java index 7f0a67a3a1..295fac94ac 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java @@ -2,7 +2,8 @@ import com.ldtteam.blockui.controls.TextField; import com.ldtteam.blockui.views.BOWindow; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.client.gui.util.InputFilters; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; @@ -10,6 +11,7 @@ import com.ldtteam.structurize.network.messages.ReplaceBlockMessage; import com.ldtteam.structurize.util.BlockUtils; import net.minecraft.client.Minecraft; +import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.item.AirItem; import net.minecraft.world.item.ItemStack; @@ -68,17 +70,17 @@ public void onSelectResource(final ItemStack to, final Integer count) missingProperties.removeAll(fromBS.getProperties()); if (!missingProperties.isEmpty()) { - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.replaceblock.ambiguous_properties", + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.replaceblock.ambiguous_properties", fromBS.getBlock().getName(), toBS.getBlock().getName(), missingProperties.stream() .map(prop -> getPropertyName(prop) + " - " + prop.getName()) - .collect(Collectors.joining(", ", "[", "]"))), false); + .collect(Collectors.joining(", ", "[", "]")))); } if (toBS.is(ModBlocks.NULL_PLACEMENT)) { - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.replaceblock.null_placement", - toBS.getBlock().getName()), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.replaceblock.null_placement", + toBS.getBlock().getName())); } final String pct = findPaneOfTypeByID("count", TextField.class).getText(); @@ -90,10 +92,10 @@ public void onSelectResource(final ItemStack to, final Integer count) catch (NumberFormatException ex) { pctNum = 100; - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.replaceblock.badpct"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.replaceblock.badpct")); } - new ReplaceBlockMessage(toReplace, to, pctNum).sendToServer(); + Network.getNetwork().sendToServer(new ReplaceBlockMessage(toReplace, to, pctNum)); } } @@ -105,7 +107,7 @@ private String getPropertyName(final Property clazz) return clazz instanceof BooleanProperty ? "Boolean" : clazz instanceof IntegerProperty ? "Integer" : clazz instanceof EnumProperty ? "Enum" - : clazz instanceof DirectionProperty ? "Direction" - : clazz.getClass().getSimpleName(); + : clazz.getValueClass() == Direction.class ? "Direction" + : clazz.getClass().getSimpleName(); } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java index c40d49a4b9..5d0594000c 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java @@ -5,27 +5,34 @@ import com.ldtteam.blockui.controls.*; import com.ldtteam.blockui.views.ScrollingList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.ItemStorage; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.ItemStorage; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.client.gui.util.InputFilters; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewRenderTask; import com.ldtteam.structurize.network.messages.*; import com.ldtteam.structurize.placement.SimplePlacementContext; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; +import com.ldtteam.structurize.util.PlacementSettings; import com.ldtteam.structurize.util.ScanToolData; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; @@ -33,14 +40,14 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; /** * Window for finishing a scan. @@ -70,7 +77,7 @@ public class WindowScan extends AbstractWindowSkeleton /** * Contains all entities needed for a certain build. */ - private final Object2IntMap> entities = new Object2IntOpenHashMap<>(); + private final Object2IntMap entities = new Object2IntOpenHashMap<>(); /** * White color. @@ -80,7 +87,7 @@ public class WindowScan extends AbstractWindowSkeleton /** * The scan tool data. */ - private ScanToolData data; + private final ScanToolData data; /** * Filter for the block and entity lists. @@ -203,11 +210,17 @@ private void fillPlaceholders() double circleRadiusMult = Double.parseDouble(findPaneOfTypeByID(INPUT_RADIUS, TextField.class).getText()); int heightOffset = Integer.parseInt(findPaneOfTypeByID(INPUT_HEIGHT_OFFSET, TextField.class).getText()); int minDistToBlocks = Integer.parseInt(findPaneOfTypeByID(INPUT_BLOCKDIST, TextField.class).getText()); - new FillTopPlaceholderMessage(data.currentSlot().box().pos1(), data.currentSlot().box().pos2(), yStretch, circleRadiusMult, heightOffset, minDistToBlocks).sendToServer(); + Network.getNetwork() + .sendToServer(new FillTopPlaceholderMessage(data.getCurrentSlotData().getBox().getPos1(), + data.getCurrentSlotData().getBox().getPos2(), + yStretch, + circleRadiusMult, + heightOffset, + minDistToBlocks)); } - catch (Exception e) + catch (NumberFormatException e) { - Minecraft.getInstance().player.displayClientMessage(Component.literal("Invalid Number"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.literal("Invalid Number")); } close(); } @@ -238,8 +251,8 @@ private void removeEntity(final Button button) final int z2 = Integer.parseInt(pos2z.getText()); final int row = entityList.getListElementIndexByPane(button); - final EntityType entity = new ArrayList<>(entities.keySet()).get(row); - new RemoveEntityMessage(new BlockPos(x1, y1, z1), new BlockPos(x2, y2, z2), EntityType.getKey(entity)).sendToServer(); + final EntityType entity = new ArrayList<>(entities.keySet()).get(row); + Network.getNetwork().sendToServer(new RemoveEntityMessage(new BlockPos(x1, y1, z1), new BlockPos(x2, y2, z2), EntityType.getKey(entity))); entities.removeInt(entity); updateEntitylist(); } @@ -248,7 +261,7 @@ private void removeBlock(final Button button) { final int row = resourceList.getListElementIndexByPane(button); final ItemPositionsStorage toRemove = allResources.get(visibleResourcesSortedList.get(row)); - new RemoveBlockMessage(toRemove).sendToServer(); + Network.getNetwork().sendToServer(new RemoveBlockMessage(toRemove)); removeAllNeededResource(toRemove.itemStorage.getItemStack()); updateResourceList(); } @@ -265,7 +278,7 @@ private Set getResources() private void removeFilteredBlock() { - new RemoveBlockMessage(allResources.values().stream().toList()).sendToServer(); + Network.getNetwork().sendToServer(new RemoveBlockMessage(allResources.values().stream().toList())); allResources.clear(); updateResourceList(); } @@ -307,7 +320,7 @@ public void onOpened() @Override public void onClosed() { - if (RenderingCache.getBoxPreviewData("scan") != null) // not confirmed/cancelled + if (RenderTaskManager.getTasksByGroup("scan") != null) // not confirmed/cancelled { updateBounds(); } @@ -335,16 +348,8 @@ public void onUpdate() */ private void discardClicked() { - RenderingCache.removeBox("scan"); - - for (Iterator> iterator = RenderingCache.boxRenderingCache.entrySet().iterator(); iterator.hasNext(); ) - { - final var entry = iterator.next(); - if (entry.getKey().contains("clickedResource")) - { - iterator.remove(); - } - } + RenderTaskManager.removeTaskGroup("scan"); + RenderTaskManager.removeTaskGroup("clickedResource"); close(); } @@ -355,49 +360,53 @@ private void confirmClicked() { updateBounds(); - new ScanOnServerMessage(data.currentSlot(), true).sendToServer(); - RenderingCache.removeBox("scan"); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + Network.getNetwork().sendToServer(new ScanOnServerMessage(slot, true)); + RenderTaskManager.removeTaskGroup("scan"); close(); } @Override - public boolean onUnhandledKeyTyped(final int ch, final int key) + public boolean onCharactedEvent(final CharacterEvent event) { + final char ch = event.codepoint() >= Character.MIN_VALUE && event.codepoint() <= Character.MAX_VALUE + ? (char) event.codepoint() + : '\0'; if (ch >= '0' && ch <= '9') { updateBounds(); - data = data.moveTo(ch - '0'); + data.moveTo(ch - '0'); loadSlot(); updateResources(); return true; } - return super.onUnhandledKeyTyped(ch, key); + return super.onCharactedEvent(event); } private void loadSlot() { - slotId.setText(String.valueOf(data.currentSlotId())); - final ScanToolData.Slot slot = data.currentSlot(); + slotId.setText(String.valueOf(data.getCurrentSlotId())); + final ScanToolData.Slot slot = data.getCurrentSlotData(); - pos1x.setText(String.valueOf(slot.box().pos1().getX())); - pos1y.setText(String.valueOf(slot.box().pos1().getY())); - pos1z.setText(String.valueOf(slot.box().pos1().getZ())); + pos1x.setText(String.valueOf(slot.getBox().getPos1().getX())); + pos1y.setText(String.valueOf(slot.getBox().getPos1().getY())); + pos1z.setText(String.valueOf(slot.getBox().getPos1().getZ())); - pos2x.setText(String.valueOf(slot.box().pos2().getX())); - pos2y.setText(String.valueOf(slot.box().pos2().getY())); - pos2z.setText(String.valueOf(slot.box().pos2().getZ())); + pos2x.setText(String.valueOf(slot.getBox().getPos2().getX())); + pos2y.setText(String.valueOf(slot.getBox().getPos2().getY())); + pos2z.setText(String.valueOf(slot.getBox().getPos2().getZ())); - RenderingCache.queue("scan", slot.box()); + RenderTaskManager.addRenderTask("scan", new BoxPreviewRenderTask("scan", slot.getBox(), 60 * 10)); findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(""); - if (!slot.name().isEmpty()) + if (!slot.getName().isEmpty()) { - findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(slot.name()); + findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(slot.getName()); } - else if (slot.box().anchor().isPresent()) + else if (slot.getBox().getAnchor().isPresent()) { - final BlockEntity tile = Minecraft.getInstance().player.level().getBlockEntity(slot.box().anchor().get()); + final BlockEntity tile = Minecraft.getInstance().player.level().getBlockEntity(slot.getBox().getAnchor().get()); if (tile instanceof IBlueprintDataProviderBE && !((IBlueprintDataProviderBE) tile).getSchematicName().isEmpty()) { findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(((IBlueprintDataProviderBE) tile).getSchematicName()); @@ -424,15 +433,15 @@ private void updateBounds() } catch (final NumberFormatException e) { - Minecraft.getInstance().player.displayClientMessage(Component.literal("Invalid Number"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.literal("Invalid Number")); return; } final String name = findPaneOfTypeByID(NAME_LABEL, TextField.class).getText(); - data = data.withCurrentSlot(new ScanToolData.Slot(name, data.currentSlot().box().withCorners(pos1, pos2))); - - RenderingCache.queue("scan", data.currentSlot().box()); - new UpdateScanToolMessage(data).sendToServer(); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + data.setCurrentSlotData(new ScanToolData.Slot(name, new BoxPreviewData(pos1, pos2, slot.getBox().getAnchor()))); + RenderTaskManager.addRenderTask("scan", new BoxPreviewRenderTask("scan", data.getCurrentSlotData().getBox(), 60 * 10)); + Network.getNetwork().sendToServer(new UpdateScanToolMessage(data)); } /** @@ -452,15 +461,20 @@ private void updateResources() return; } - final BoxPreviewData box = data.currentSlot().box(); - final List list = world.getEntitiesOfClass(Entity.class, AABB.encapsulatingFullBlocks(box.pos1(), box.pos2())); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + + final List list = world.getEntitiesOfClass( + Entity.class, + new AABB( + Vec3.atLowerCornerOf(slot.getBox().getPos1()), + Vec3.atLowerCornerOf(slot.getBox().getPos2()))); for (final Entity entity : list) { // LEASH_KNOT, while not directly serializable, still serializes as part of the mob // and drops a lead, so we should alert builders that it exists in the scan if (!entities.containsKey(entity.getName().getString()) - && (entity.getType().canSerialize() || entity.getType().equals(EntityType.LEASH_KNOT)) + && (entity.getType().canSerialize() || entity.getType().equals(EntityTypes.LEASH_KNOT)) && (filter.isEmpty() || (entity.getName().getString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)) || (entity.toString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)))))) { @@ -469,13 +483,12 @@ private void updateResources() } final BlockPos.MutableBlockPos here = new BlockPos.MutableBlockPos(); - final int minX = Math.min(box.pos1().getX(), box.pos2().getX()); - final int minY = Math.min(box.pos1().getY(), box.pos2().getY()); - final int minZ = Math.min(box.pos1().getZ(), box.pos2().getZ()); - final int maxX = Math.max(box.pos1().getX(), box.pos2().getX()); - final int maxY = Math.max(box.pos1().getY(), box.pos2().getY()); - final int maxZ = Math.max(box.pos1().getZ(), box.pos2().getZ()); - final BoundingBox boundingBox = BoundingBox.fromCorners(box.pos1(), box.pos2()); + final int minX = Math.min(slot.getBox().getPos1().getX(), slot.getBox().getPos2().getX()); + final int minY = Math.min(slot.getBox().getPos1().getY(), slot.getBox().getPos2().getY()); + final int minZ = Math.min(slot.getBox().getPos1().getZ(), slot.getBox().getPos2().getZ()); + final int maxX = Math.max(slot.getBox().getPos1().getX(), slot.getBox().getPos2().getX()); + final int maxY = Math.max(slot.getBox().getPos1().getY(), slot.getBox().getPos2().getY()); + final int maxZ = Math.max(slot.getBox().getPos1().getZ(), slot.getBox().getPos2().getZ()); for (int x = minX; x <= maxX; x++) { @@ -508,9 +521,7 @@ private void updateResources() else { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); - final List itemList = - handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(world.registryAccess()), - new SimplePlacementContext(false, RotationMirror.NONE)); + final List itemList = handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), new SimplePlacementContext(false, new PlacementSettings())); for (final ItemStack stack : itemList) { addNeededResource(stack, visible, here); @@ -551,7 +562,7 @@ public void addNeededResource(@Nullable final ItemStack res, final boolean visib } if (filter.isEmpty() - || res.getDescriptionId().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)) + || res.getItem().getDescriptionId().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)) || res.getHoverName().getString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US))) { final ItemStorage stackToStore = new ItemStorage(res, 1, true, false); @@ -570,7 +581,7 @@ public void updateEntitylist() { entityList.enable(); entityList.show(); - final List> tempEntities = new ArrayList<>(entities.keySet()); + final List tempEntities = new ArrayList<>(entities.keySet()); //Creates a dataProvider for the unemployed resourceList. entityList.setDataProvider(new ScrollingList.DataProvider() @@ -594,17 +605,17 @@ public int getElementCount() @Override public void updateElement(final int index, final Pane rowPane) { - final EntityType entity = tempEntities.get(index); - ItemStack entityIcon = entity.create(Minecraft.getInstance().level).getPickResult(); - if (entity == EntityType.GLOW_ITEM_FRAME) + final EntityType entity = tempEntities.get(index); + ItemStack entityIcon = entity.create(Minecraft.getInstance().level, EntitySpawnReason.LOAD).getPickResult(); + if (entity == EntityTypes.GLOW_ITEM_FRAME) { entityIcon = new ItemStack(Items.GLOW_ITEM_FRAME); } - else if (entity == EntityType.ITEM_FRAME) + else if (entity == EntityTypes.ITEM_FRAME) { entityIcon = new ItemStack(Items.ITEM_FRAME); } - else if (entity == EntityType.MINECART) + else if (entity == EntityTypes.MINECART) { entityIcon = new ItemStack(Items.MINECART); } @@ -680,9 +691,9 @@ private void doHighLightBlocks(Button button, final ItemStorage block) final ItemPositionsStorage itemPositionsStorage = allResources.get(block); for (final BlockPos position : itemPositionsStorage.positions) { - BoxPreviewData previewData = new BoxPreviewData(position, position, Optional.empty()); - previewData.setExpireTime(30); - RenderingCache.queue("clickedResource" + position.toShortString(), previewData); + BoxPreviewRenderTask previewData = + new BoxPreviewRenderTask("clickedResource" + position.toShortString(), new BoxPreviewData(position, position, Optional.empty()), 30); + RenderTaskManager.addRenderTask("clickedResource", previewData); } window.close(); } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java index 51b155583e..e9b4b47c52 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java @@ -6,10 +6,10 @@ import com.ldtteam.blockui.views.BOWindow; import com.ldtteam.blockui.views.ScrollingList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.client.gui.util.InputFilters; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import org.apache.commons.lang3.StringUtils; @@ -22,7 +22,7 @@ import java.util.Locale; import java.util.function.BiConsumer; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * Window to select a resource from a given list of items @@ -107,7 +107,7 @@ public WindowSelectRes( final List allItems, final BiConsumer resultAction) { - this(ResourceLocation.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), origin, description, previousItem, allItems, resultAction, false, null); + this(Identifier.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), origin, description, previousItem, allItems, resultAction, false, null); } public WindowSelectRes( @@ -119,18 +119,11 @@ public WindowSelectRes( final boolean secondaryConfirm, @Nullable Component selectCountText) { - this(ResourceLocation.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), - origin, - description, - previousItem, - allItems, - resultAction, - secondaryConfirm, - selectCountText); + this(Identifier.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), origin, description, previousItem, allItems, resultAction, secondaryConfirm, selectCountText); } public WindowSelectRes( - final ResourceLocation xml, + final Identifier xml, @Nullable final BOWindow origin, final Component description, @Nullable final ItemStack previousItem, @@ -323,7 +316,7 @@ private void updateResources() for (final ItemStack stack : allItems) { if ((this.filter.isEmpty() - || stack.getDescriptionId().toLowerCase(Locale.US).contains(this.filter.toLowerCase(Locale.US)) + || stack.getItem().getDescriptionId().toLowerCase(Locale.US).contains(this.filter.toLowerCase(Locale.US)) || stack.getHoverName().getString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)))) { this.displayedItems.add(stack); diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java index 53e4d76fa0..9b3a612795 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java @@ -6,10 +6,10 @@ import com.ldtteam.blockui.controls.ToggleButton; import com.ldtteam.blockui.views.DropDownList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.Shape; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Shape; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; import com.ldtteam.structurize.client.gui.util.ItemUtil; @@ -21,13 +21,13 @@ import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; @@ -36,9 +36,9 @@ import java.util.List; import java.util.Locale; -import static com.ldtteam.structurize.api.constants.Constants.BLUEPRINT_FOLDER; -import static com.ldtteam.structurize.api.constants.Constants.SHAPES_FOLDER; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.BLUEPRINT_FOLDER; +import static com.ldtteam.structurize.api.util.constant.Constants.SHAPES_FOLDER; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; /** * BuildTool window. @@ -80,7 +80,7 @@ public class WindowShapeTool extends AbstractBlueprintManipulationWindow /** * List of section. */ - private final List> sections = new ArrayList<>(); + private final List sections = new ArrayList<>(); /** * Drop down list for section. @@ -125,8 +125,6 @@ public class WindowShapeTool extends AbstractBlueprintManipulationWindow */ private String shapeequation = ""; - private final HolderLookup.Provider provider; - /** * Creates a window inputShape tool. * This requires X, Y and Z coordinates. @@ -135,10 +133,9 @@ public class WindowShapeTool extends AbstractBlueprintManipulationWindow * * @param pos coordinate. */ - public WindowShapeTool(@Nullable final BlockPos pos, final HolderLookup.Provider provider) + public WindowShapeTool(@Nullable final BlockPos pos) { - super(Constants.MOD_ID + SHAPE_TOOL_RESOURCE_SUFFIX, pos,0, "shapes"); - this.provider = provider; + super(Constants.MOD_ID + SHAPE_TOOL_RESOURCE_SUFFIX, pos, 0, "shapes"); this.init(pos, false); //todo placement handler support as well } @@ -189,7 +186,7 @@ private void init(final BlockPos pos, final boolean shouldUpdate) registerButton(INPUT_FREQUENCY + BUTTON_PLUS, () -> adjust(inputFrequency, frequency + 1)); sections.clear(); - Arrays.stream(Shape.values()).map(s -> new Tuple<>(s, Component.translatable("structurize.shapetool.shape." + s.name().toLowerCase()))).forEach(sections::add); + sections.addAll(Arrays.stream(Shape.values()).map(Enum::name).toList()); sectionsDropDownList = findPaneOfTypeByID(DROPDOWN_STYLE_ID, DropDownList.class); sectionsDropDownList.setHandler(this::onDropDownListChanged); @@ -212,7 +209,7 @@ private void init(final BlockPos pos, final boolean shouldUpdate) /** * Generate the inputShape depending on the variables on the client. */ - private void genShape() + private static void genShape() { RenderingCache.getOrCreateBlueprintPreviewData("shapes").setBlueprint(Manager.getStructureFromFormula( width, @@ -223,8 +220,7 @@ private void genShape() shape, mainBlock, secondaryBlock, - hollow, - provider)); + hollow)); } private void disableInputIfNecessary() @@ -312,7 +308,7 @@ public int getElementCount() @Override public MutableComponent getLabel(final int index) { - return sections.get(index).getB(); + return Component.literal(sections.get(index)); } } @@ -347,19 +343,20 @@ protected void handlePlacement(final BuildToolPlacementMessage.HandlerType type, final CompoundTag compound = BlueprintUtil.writeBlueprintToNBT(previewData.getBlueprint()); ClientFutureProcessor.queueBlueprint( - new ClientFutureProcessor.BlueprintProcessingData(StructurePacks.storeBlueprint(packName, compound, path, provider), blueprint -> - new BuildToolPlacementMessage( - type, - id, - packName, - subpath.toString(), - previewData.getPos(), - RotationMirror.NONE).sendToServer())); - - if (type == BuildToolPlacementMessage.HandlerType.Survival) - { - clearAndClose(); - } + new ClientFutureProcessor.BlueprintProcessingData(StructurePacks.storeBlueprint(packName, compound, path), blueprint -> + Network.getNetwork().sendToServer(new BuildToolPlacementMessage( + type, + id, + packName, + subpath.toString(), + previewData.getPos(), + Rotation.NONE, + Mirror.NONE)))); + + // The generated shape is consumed by every placement mode. Clear + // its preview after the request so Complete/Pretty cannot leave a + // stale ghost in the world. + clearAndClose(); } } @@ -448,7 +445,7 @@ private void onDropDownListChanged(final DropDownList list) { if (list == sectionsDropDownList) { - updateStyle(sections.get(sectionsDropDownList.getSelectedIndex()).getA()); + updateStyle(sections.get(sectionsDropDownList.getSelectedIndex())); } } @@ -457,11 +454,11 @@ private void onDropDownListChanged(final DropDownList list) * * @param s the style to use. */ - private void updateStyle(final Shape newShape) + private void updateStyle(final String s) { - if (newShape != shape) + if (Shape.valueOf(sections.get(sectionsDropDownList.getSelectedIndex())) != shape) { - shape = newShape; + shape = Shape.valueOf(s); genShape(); } disableInputIfNecessary(); diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java index 153e9255fc..75f94afead 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java @@ -11,8 +11,8 @@ import com.ldtteam.blockui.views.BOWindow; import com.ldtteam.blockui.views.Box; import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.storage.StructurePackMeta; import com.ldtteam.structurize.storage.StructurePacks; import com.ldtteam.structurize.util.IOPool; @@ -29,10 +29,10 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; -import static com.ldtteam.structurize.api.constants.TranslationConstants.GUI_SWITCH_PACK_AUTHORS; -import static com.ldtteam.structurize.api.constants.TranslationConstants.GUI_SWITCH_PACK_DISABLED_TEXT; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.GUI_SWITCH_PACK_AUTHORS; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.GUI_SWITCH_PACK_DISABLED_TEXT; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; import static com.ldtteam.structurize.config.ServerConfiguration.CONFIG_OPTION_ALLOW_PLAYER_SCHEMATICS; /** diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java index abe5937cd3..a251c934d5 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java @@ -4,16 +4,21 @@ import com.ldtteam.blockui.PaneBuilders; import com.ldtteam.blockui.controls.*; import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.structurize.api.TagManager; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; -import com.ldtteam.structurize.items.ItemTagTool.TagData; +import com.ldtteam.structurize.items.ItemTagTool; import com.ldtteam.structurize.network.messages.AddRemoveTagMessage; import com.ldtteam.structurize.network.messages.SetTagInTool; import com.ldtteam.structurize.util.BlockUtils; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import com.ldtteam.structurize.util.TagManager; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.client.input.KeyEvent; import net.minecraft.network.chat.Component; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; @@ -127,14 +132,25 @@ public void close() { super.close(); currentTag = findPaneOfTypeByID(INPUT_FIELD, TextField.class).getText(); - TagData.updateItemStack(stack, tags -> tags.setCurrentTag(currentTag)); - new SetTagInTool(currentTag, Minecraft.getInstance().player.getInventory().findSlotMatchingItem(stack)).sendToServer(); + final CompoundTag itemTag = ItemStackNbtHelper.getOrCreateCustomTag(stack); + itemTag.putString(ItemTagTool.TAG_CURRENT_TAG, currentTag); + ItemStackNbtHelper.setCustomTag(stack, itemTag); + Network.getNetwork().sendToServer(new SetTagInTool(currentTag, Minecraft.getInstance().player.getInventory().findSlotMatchingItem(stack))); } @Override - public boolean onKeyTyped(final char ch, final int key) + public boolean onKeyEvent(final KeyEvent event) { - final boolean returnValue = super.onKeyTyped(ch, key);; + final boolean returnValue = super.onKeyEvent(event); + updateTagOptionList(); + currentTag = findPaneOfTypeByID(INPUT_FIELD, TextField.class).getText(); + return returnValue; + } + + @Override + public boolean onCharactedEvent(final CharacterEvent event) + { + final boolean returnValue = super.onCharactedEvent(event); updateTagOptionList(); currentTag = findPaneOfTypeByID(INPUT_FIELD, TextField.class).getText(); return returnValue; @@ -159,7 +175,7 @@ private void removeTag(final Button button) { String tag = map.get(toRemove).get(map.get(toRemove).size() - 1); dataTE.removeTag(toRemove, tag); - new AddRemoveTagMessage(false, tag, toRemove, anchorPos).sendToServer(); + Network.getNetwork().sendToServer(new AddRemoveTagMessage(false, tag, toRemove, anchorPos)); } updateResourceList(); } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java index de6748daf7..ab831dd074 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java @@ -4,18 +4,19 @@ import com.ldtteam.blockui.controls.Button; import com.ldtteam.blockui.controls.Text; import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.network.messages.OperationHistoryMessage; import com.ldtteam.structurize.network.messages.UndoRedoMessage; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; import static com.ldtteam.structurize.client.gui.WindowScan.WHITE; import static com.ldtteam.structurize.operations.UndoOperation.UNDO_PREFIX; @@ -54,7 +55,7 @@ private void undoRedoClicked(final Button button, boolean undo) { final int index = operationsList.getListElementIndexByPane(button); final Tuple operation = lastOperations.get(index); - new UndoRedoMessage(operation.getB(), undo).sendToServer(); + Network.getNetwork().sendToServer(new UndoRedoMessage(operation.getB(), undo)); close(); } @@ -124,6 +125,6 @@ public void open() { super.open(); setVisible(true); - new OperationHistoryMessage().sendToServer(); + Network.getNetwork().sendToServer(new OperationHistoryMessage()); } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java index e77b2d9331..edce8828d2 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java @@ -1,9 +1,12 @@ package com.ldtteam.structurize.client.gui.util; -import com.ldtteam.structurize.api.ItemStorage; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStorage; +import com.ldtteam.structurize.api.util.Log; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.BlockPos; import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import java.util.ArrayList; import java.util.List; @@ -22,9 +25,9 @@ public ItemPositionsStorage(final ItemStorage itemStorage) this.itemStorage = itemStorage; } - public ItemPositionsStorage(final RegistryFriendlyByteBuf buf) + public ItemPositionsStorage(final FriendlyByteBuf buf) { - itemStorage = new ItemStorage(buf); + itemStorage = new ItemStorage(wrapRegistry(buf)); final int count = buf.readVarInt(); for (int i = 0; i < count; i++) @@ -85,9 +88,9 @@ public int hashCode() * * @param buf */ - public void serialize(final RegistryFriendlyByteBuf buf) + public void serialize(final FriendlyByteBuf buf) { - itemStorage.serialize(buf); + itemStorage.serialize(wrapRegistry(buf)); buf.writeVarInt(positions.size()); for (final BlockPos pos : positions) { @@ -96,4 +99,11 @@ public void serialize(final RegistryFriendlyByteBuf buf) buf.writeVarInt(pos.getZ()); } } + + private static RegistryFriendlyByteBuf wrapRegistry(final FriendlyByteBuf buf) + { + return new RegistryFriendlyByteBuf( + buf, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java index 59432a4a7a..1c223ee871 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.client.gui.util; import com.google.common.collect.ImmutableList; -import com.ldtteam.structurize.api.ItemStorage; +import com.ldtteam.structurize.api.util.ItemStorage; import net.minecraft.client.Minecraft; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.*; @@ -48,11 +48,11 @@ public static List getAllItemsInlcudingInventory() } } - for (final ItemStack stack : Minecraft.getInstance().player.getInventory().items) + for (final ItemStack stack : Minecraft.getInstance().player.getInventory().getNonEquipmentItems()) { final Item item = stack.getItem(); if (item instanceof AirItem || item instanceof BlockItem || (item instanceof BucketItem - && ((BucketItem) item).content != Fluids.EMPTY)) + && ((BucketItem) item).getContent() != Fluids.EMPTY)) { items.add(new ItemStorage(stack.copy())); } diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidBakedModel.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidBakedModel.java deleted file mode 100644 index ba8f5c925f..0000000000 --- a/src/main/java/com/ldtteam/structurize/client/model/OverlaidBakedModel.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ldtteam.structurize.client.model; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.world.item.ItemDisplayContext; -import net.neoforged.neoforge.client.model.BakedModelWrapper; -import org.jetbrains.annotations.NotNull; - -/** - * This exists because it seems to be the only way to override {@link #isCustomRenderer}... - */ -public class OverlaidBakedModel extends BakedModelWrapper -{ - public OverlaidBakedModel(@NotNull final BakedModel overlay) - { - super(overlay); - } - - @Override - public boolean isCustomRenderer() - { - return true; - } - - @NotNull - @Override - public BakedModel applyTransform(@NotNull final ItemDisplayContext transformType, - @NotNull final PoseStack poseStack, - final boolean applyLeftHandTransform) - { - return new OverlaidBakedModel(originalModel.applyTransform(transformType, poseStack, applyLeftHandTransform)); - } -} diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidGeometry.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidGeometry.java deleted file mode 100644 index 4ac2c2348b..0000000000 --- a/src/main/java/com/ldtteam/structurize/client/model/OverlaidGeometry.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.ldtteam.structurize.client.model; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.*; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import java.util.function.Function; - -/** - * Simple wrapper to create {@link OverlaidBakedModel}. - */ -public class OverlaidGeometry implements IUnbakedGeometry -{ - private ResourceLocation overlayModelId; - - public OverlaidGeometry(final ResourceLocation overlayModelId) - { - this.overlayModelId = overlayModelId; - } - - @Override - public BakedModel bake( - final IGeometryBakingContext context, - final ModelBaker baker, - final Function spriteGetter, - final ModelState modelState, - final ItemOverrides overrides) - { - UnbakedModel unbaked = baker.getModel(overlayModelId); - BakedModel baked = unbaked.bake(baker, spriteGetter, modelState); - - if (baked == null) - { - baked = Minecraft.getInstance().getModelManager().getMissingModel(); - } - - return new OverlaidBakedModel(baked); - } -} diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java index a7a5edd183..321f95901c 100644 --- a/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java +++ b/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java @@ -1,25 +1,17 @@ -package com.ldtteam.structurize.client.model; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import org.jetbrains.annotations.NotNull; - -/** - * Simple loader to create {@link OverlaidGeometry}. - */ -public class OverlaidModelLoader implements IGeometryLoader -{ - @NotNull - @Override - public OverlaidGeometry read(@NotNull JsonObject jsonObject, - @NotNull JsonDeserializationContext deserializationContext) throws JsonParseException - { - final String parent = jsonObject.get("parent").getAsString(); - final ResourceLocation parentLocation = ResourceLocation.parse(parent); - - return new OverlaidGeometry(parentLocation); - } -} +package com.ldtteam.structurize.client.model; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import net.minecraft.resources.Identifier; +import net.neoforged.neoforge.client.model.UnbakedModelLoader; + +public class OverlaidModelLoader implements UnbakedModelLoader +{ + @Override + public OverlaidUnbakedModel read(final JsonObject jsonObject, final JsonDeserializationContext context) + throws JsonParseException + { + return new OverlaidUnbakedModel(Identifier.parse(jsonObject.get("parent").getAsString())); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidUnbakedModel.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidUnbakedModel.java new file mode 100644 index 0000000000..c5e265d29c --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/model/OverlaidUnbakedModel.java @@ -0,0 +1,76 @@ +package com.ldtteam.structurize.client.model; + +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelDebugName; +import net.minecraft.client.resources.model.ResolvedModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.renderer.block.dispatch.ModelState; +import net.minecraft.client.resources.model.geometry.QuadCollection; +import net.minecraft.client.resources.model.geometry.UnbakedGeometry; +import net.minecraft.client.resources.model.sprite.TextureSlots; +import net.minecraft.resources.Identifier; +import net.minecraft.util.context.ContextMap; + +/** + * Loads and bakes the parent model used by Structurize's overlaid model JSON. + */ +public class OverlaidUnbakedModel implements UnbakedModel +{ + private final Identifier parentModelId; + + public OverlaidUnbakedModel(final Identifier parentModelId) + { + this.parentModelId = parentModelId; + } + + @Override + public UnbakedGeometry geometry() + { + return new UnbakedGeometry() + { + @Override + public QuadCollection bake( + final TextureSlots textureSlots, + final ModelBaker baker, + final ModelState state, + final ModelDebugName name) + { + return bakeParent(baker, state, ContextMap.EMPTY); + } + + @Override + public QuadCollection bake( + final TextureSlots textureSlots, + final ModelBaker baker, + final ModelState state, + final ModelDebugName name, + final ContextMap properties) + { + return bakeParent(baker, state, properties); + } + }; + } + + private QuadCollection bakeParent(final ModelBaker baker, final ModelState state, final ContextMap properties) + { + final ResolvedModel parent = baker.getModel(parentModelId); + return parent.getTopGeometry().bake(parent.getTopTextureSlots(), baker, state, parent, properties); + } + + @Override + public void resolveDependencies(final Resolver resolver) + { + resolver.markDependency(parentModelId); + } + + /** + * Keep the wrapped overlay model in the 26.2 parent chain so vanilla can + * resolve its texture slots (including the particle material) before the + * custom geometry is baked. + */ + @Override + public Identifier parent() + { + return parentModelId; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendercontext/WorldEventRenderContext.java b/src/main/java/com/ldtteam/structurize/client/rendercontext/WorldEventRenderContext.java new file mode 100644 index 0000000000..dd4cce6400 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendercontext/WorldEventRenderContext.java @@ -0,0 +1,71 @@ +package com.ldtteam.structurize.client.rendercontext; + +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import com.ldtteam.structurize.client.rendertask.util.BufferSourceCompat; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +/** + * Main class for handling world rendering. + * Also holds all possible values which may be needed during rendering. + */ +public class WorldEventRenderContext +{ + public static final WorldEventRenderContext INSTANCE = new WorldEventRenderContext(); + + private WorldEventRenderContext() + { + // singleton + } + + public RenderLevelStageEvent stageEvent; + public BufferSourceCompat bufferSource; + public PoseStack poseStack; + public float partialTicks; + public ClientLevel clientLevel; + public LocalPlayer clientPlayer; + public ItemStack mainHandItem; + + /** + * In chunks + */ + int clientRenderDist; + + public boolean isStage(final Class stageType) + { + return stageType.isInstance(stageEvent); + } + + public void renderWorldLastEvent(final RenderLevelStageEvent event) + { + stageEvent = event; + bufferSource = WorldRenderMacros.getBufferSource(); + poseStack = event.getPoseStack(); + partialTicks = Minecraft.getInstance().getDeltaTracker().getGameTimeDeltaPartialTick(false); + clientLevel = Minecraft.getInstance().level; + clientPlayer = Minecraft.getInstance().player; + mainHandItem = clientPlayer.getMainHandItem(); + clientRenderDist = Minecraft.getInstance().options.renderDistance().get(); + + final Vec3 cameraPos = Minecraft.getInstance().gameRenderer.mainCamera().position(); + poseStack.pushPose(); + poseStack.translate(-cameraPos.x(), -cameraPos.y(), -cameraPos.z()); + + runRenderTasks(event); + + bufferSource.endBatch(); + + poseStack.popPose(); + } + + private void runRenderTasks(final RenderLevelStageEvent event) + { + RenderTaskManager.render(this); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/RenderTaskManager.java b/src/main/java/com/ldtteam/structurize/client/rendertask/RenderTaskManager.java new file mode 100644 index 0000000000..d58da1fc25 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/RenderTaskManager.java @@ -0,0 +1,118 @@ +package com.ldtteam.structurize.client.rendertask; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.task.IRenderTask; + +import javax.annotation.Nullable; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +public class RenderTaskManager +{ + // Uses: + // Render a box with: Text, color, duration, transparency, infront/behind blocks + // Render text at a position(box optional?) + // Render a large box in world(claim borders, lumberjack etc) + // Timed task: enables/disables citizen glowing effect with duration + + /** + * A position to highlight with a group and key. + */ + private static final Map> RENDER_TASKS = new LinkedHashMap<>(); + + /** + * Highlights positions + * + * @param context rendering context + */ + public static void render(final WorldEventRenderContext context) + { + if (RENDER_TASKS.isEmpty()) + { + return; + } + + for (final Iterator> groups = RENDER_TASKS.values().iterator(); groups.hasNext(); ) + { + final Map group = groups.next(); + for (final Iterator renderTaskIterator = group.values().iterator(); renderTaskIterator.hasNext(); ) + { + final IRenderTask renderTask = renderTaskIterator.next(); + + if (renderTask.shouldRenderIn(context.stageEvent.getClass())) + { + renderTask.render(context); + } + } + } + } + + /** + * Client tick callback + */ + public static void onClientTick() + { + for (final Iterator> groups = RENDER_TASKS.values().iterator(); groups.hasNext(); ) + { + final Map group = groups.next(); + for (final Iterator containers = group.values().iterator(); containers.hasNext(); ) + { + final IRenderTask renderDataContainer = containers.next(); + boolean isDone = renderDataContainer.tick(); + if (isDone) + { + containers.remove(); + } + } + + if (group.isEmpty()) + { + groups.remove(); + } + } + } + + /** + * Clears all highlight items for the given group key. + * + * @param key the key to remove the render data for. + */ + public static void clearHighlightsForKey(final String key) + { + RENDER_TASKS.remove(key); + } + + /** + * Adds a highlight item for the given key. + * + * @param key the group key of the item to render. + * @return the previous entry or null + */ + public static IRenderTask addRenderTask(final String key, final IRenderTask task) + { + return RENDER_TASKS.computeIfAbsent(key, k -> new LinkedHashMap<>()).put(task.id(), task); + } + + @Nullable + public static Map getTasksByGroup(final String groupID) + { + return RENDER_TASKS.get(groupID); + } + + public static boolean removeTaskGroup(final String groupID) + { + return RENDER_TASKS.remove(groupID) != null; + } + + public static boolean removeTaskEntry(final String groupID, final String taskID) + { + Map entry = RENDER_TASKS.get(groupID); + if (entry != null) + { + return entry.remove(taskID) != null; + } + + return false; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/IClientTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IClientTask.java new file mode 100644 index 0000000000..e41353577e --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IClientTask.java @@ -0,0 +1,6 @@ +package com.ldtteam.structurize.client.rendertask.task; + +public interface IClientTask extends ITickingTask +{ + +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/IRenderTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IRenderTask.java new file mode 100644 index 0000000000..faef21114c --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IRenderTask.java @@ -0,0 +1,19 @@ +package com.ldtteam.structurize.client.rendertask.task; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +public interface IRenderTask extends IClientTask +{ + /** + * Indicate the render data it should continue rendering. + */ + void render(final WorldEventRenderContext context); + + /** + * Precheck for the render stage, as render(WorldEventRenderContext) is run for all stages + * + * @return true if rendering active for this stage + */ + boolean shouldRenderIn(Class renderStage); +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/ITickingTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/ITickingTask.java new file mode 100644 index 0000000000..071c88975b --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/ITickingTask.java @@ -0,0 +1,18 @@ +package com.ldtteam.structurize.client.rendertask.task; + +public interface ITickingTask +{ + /** + * Client Tick callback + * + * @return true if task finished, false if not + */ + public boolean tick(); + + /** + * Task string identifier + * + * @return + */ + public String id(); +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/TimedTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/TimedTask.java new file mode 100644 index 0000000000..d1d1737e71 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/TimedTask.java @@ -0,0 +1,39 @@ +package com.ldtteam.structurize.client.rendertask.task; + +public abstract class TimedTask implements IClientTask +{ + private final String id; + private int ticksLeft = 0; + + protected TimedTask(final String id, final int seconds) + { + this.id = id; + this.ticksLeft = seconds * 20; + } + + /** + * Duration of the task + */ + public void setDurationSeconds(final int seconds) + { + this.ticksLeft = seconds * 20; + } + + public boolean isExpired() + { + return ticksLeft <= 0; + } + + @Override + public boolean tick() + { + ticksLeft--; + return isExpired(); + } + + @Override + public String id() + { + return id; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewData.java b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewData.java new file mode 100644 index 0000000000..93c933de40 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewData.java @@ -0,0 +1,54 @@ +package com.ldtteam.structurize.client.rendertask.tasks; + +import net.minecraft.core.BlockPos; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * Preview data for box contexts. + */ +public class BoxPreviewData +{ + @NotNull + private final BlockPos pos1; + + @NotNull + private final BlockPos pos2; + + @NotNull + private Optional anchor; + + /** + * Create a new box. + * @param pos1 the first pos. + * @param pos2 the second pos. + * @param anchor the anchor of the box. + */ + public BoxPreviewData(final @NotNull BlockPos pos1, final @NotNull BlockPos pos2, final @NotNull Optional anchor) + { + this.pos1 = pos1; + this.pos2 = pos2; + this.anchor = anchor; + } + + public BlockPos getPos1() + { + return pos1; + } + + public BlockPos getPos2() + { + return pos2; + } + + public Optional getAnchor() + { + return anchor; + } + + public void setAnchor(final Optional anchor) + { + this.anchor = anchor; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewRenderTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewRenderTask.java new file mode 100644 index 0000000000..ed6098f1ba --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewRenderTask.java @@ -0,0 +1,38 @@ +package com.ldtteam.structurize.client.rendertask.tasks; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.task.IRenderTask; +import com.ldtteam.structurize.client.rendertask.task.TimedTask; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +/** + * Preview data for box contexts. + */ +public class BoxPreviewRenderTask extends TimedTask implements IRenderTask +{ + private final BoxPreviewData data; + + /** + * Create a new box preview task + */ + public BoxPreviewRenderTask(final String id, final BoxPreviewData data, final int seconds) + { + super(id, seconds); + this.data = data; + } + + @Override + public void render(final WorldEventRenderContext context) + { + // Used to render a red box around a scan's Primary offset (primary block) + WorldRenderMacros.renderWhiteLineBox(context.bufferSource, context.poseStack, data.getPos1(), data.getPos2(), 0.025f); + data.getAnchor().map(pos -> pos).ifPresent(pos -> WorldRenderMacros.renderRedGlintLineBox(context.bufferSource, context.poseStack, pos, pos, 0.025f)); + } + + @Override + public boolean shouldRenderIn(final Class renderStage) + { + return renderStage == RenderLevelStageEvent.AfterOpaqueFeatures.class; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/TimedBlockRenderTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/TimedBlockRenderTask.java new file mode 100644 index 0000000000..0b76e3460f --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/TimedBlockRenderTask.java @@ -0,0 +1,89 @@ +package com.ldtteam.structurize.client.rendertask.tasks; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.task.IRenderTask; +import com.ldtteam.structurize.client.rendertask.task.TimedTask; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; +import net.minecraft.core.BlockPos; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +import java.util.ArrayList; +import java.util.List; + +import static com.ldtteam.structurize.client.rendertask.util.RenderTypes.LINES_WITH_WIDTH; + +/** + * Highlight render data for marking blocks in the world with potential warnings on them. + */ +public class TimedBlockRenderTask extends TimedTask implements IRenderTask +{ + /** + * List of texts to display. + */ + private final List text = new ArrayList<>(); + + /** + * Position where to render the box. + */ + private final BlockPos pos; + + /** + * The colour at which the box should render. + */ + private int argbColor = 0xffffffff; + + /** + * Default constructor. + */ + public TimedBlockRenderTask(final String id, final BlockPos pos, final int seconds) + { + super(id, seconds); + this.pos = pos; + } + + @Override + public void render(final WorldEventRenderContext context) + { + if (context.clientPlayer.blockPosition().distSqr(pos) > 50 * 50) + { + return; + } + + WorldRenderMacros.renderLineBox(context.bufferSource.getBuffer(LINES_WITH_WIDTH), + context.poseStack, + pos, + pos, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + 0.025f); + + if (!text.isEmpty()) + { + WorldRenderMacros.renderDebugText(pos, text, context.poseStack, true, 3, context.bufferSource); + } + } + + @Override + public boolean shouldRenderIn(final Class renderStage) + { + return renderStage == RenderLevelStageEvent.AfterOpaqueFeatures.class; + } + + /** + * List of strings to display + */ + public void addText(final String text) + { + this.text.add(text); + } + + /** + * Color code for the box, argb format + */ + public void setColor(final int argbColor) + { + this.argbColor = argbColor; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/util/BufferSourceCompat.java b/src/main/java/com/ldtteam/structurize/client/rendertask/util/BufferSourceCompat.java new file mode 100644 index 0000000000..d6a0d84fd1 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/util/BufferSourceCompat.java @@ -0,0 +1,38 @@ +package com.ldtteam.structurize.client.rendertask.util; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.rendertype.RenderType; + +import java.util.HashMap; +import java.util.Map; + +/** + * Compatibility shim replacing the removed MultiBufferSource.BufferSource. + * Manages per-RenderType BufferBuilder instances for immediate-mode rendering. + */ +public final class BufferSourceCompat +{ + private static final int DEFAULT_BUFFER_SIZE = 1024; + + private final Map builders = new HashMap<>(); + private final ByteBufferBuilder fallbackBuffer = new ByteBufferBuilder(DEFAULT_BUFFER_SIZE); + + public BufferSourceCompat() + { + } + + public VertexConsumer getBuffer(final RenderType renderType) + { + return builders.computeIfAbsent(renderType, type -> new BufferBuilder( + new ByteBufferBuilder(RenderType.BIG_BUFFER_SIZE), + type.primitiveTopology(), + type.format())); + } + + public void endBatch() + { + builders.clear(); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/util/RenderTypes.java b/src/main/java/com/ldtteam/structurize/client/rendertask/util/RenderTypes.java new file mode 100644 index 0000000000..0f4ff21c91 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/util/RenderTypes.java @@ -0,0 +1,139 @@ +package com.ldtteam.structurize.client.rendertask.util; + +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.ColorTargetState; +import com.mojang.blaze3d.pipeline.DepthStencilState; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.CompareOp; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.rendertype.RenderSetup; +import net.minecraft.resources.Identifier; +import net.minecraft.util.Util; + +import java.util.function.Function; + +/** + * Structurize's overlay render types, expressed with the current render pipeline API. + */ +public final class RenderTypes +{ + private RenderTypes() + { + } + + public static RenderType worldEntityIcon(final Identifier texture) + { + return WORLD_ENTITY_ICON.apply(texture); + } + + public static final RenderType LINES_OUTSIDE_BLOCKS = positionColor( + "structurize:lines_outside_blocks", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + false, + true, + 1024); + + public static final RenderType LINES_INSIDE_BLOCKS = positionColor( + "structurize:lines_inside_blocks", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.GREATER_THAN, + false, + true, + 1024); + + public static final RenderType GLINT_LINES = positionColor( + "structurize_glint_lines", + com.mojang.blaze3d.PrimitiveTopology.DEBUG_LINES, + BlendFunction.ADDITIVE, + CompareOp.ALWAYS_PASS, + false, + false, + 1 << 12); + + public static final RenderType GLINT_LINES_WITH_WIDTH = positionColor( + "structurize_glint_lines_with_width", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.ADDITIVE, + CompareOp.ALWAYS_PASS, + true, + true, + 1 << 13); + + public static final RenderType LINES = positionColor( + "structurize_lines", + com.mojang.blaze3d.PrimitiveTopology.DEBUG_LINES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + false, + false, + 1 << 14); + + public static final RenderType LINES_WITH_WIDTH = positionColor( + "structurize_lines_with_width", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + true, + true, + 1 << 13); + + public static final RenderType COLORED_TRIANGLES = positionColor( + "structurize_colored_triangles", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + true, + true, + 1 << 13); + + public static final RenderType COLORED_TRIANGLES_NC_ND = positionColor( + "structurize_colored_triangles_nc_nd", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.ALWAYS_PASS, + false, + false, + 1 << 12); + + private static final Function WORLD_ENTITY_ICON = Util.memoize(texture -> { + final RenderPipeline pipeline = RenderPipeline.builder(RenderPipelines.GLOBALS_SNIPPET) + .withLocation(Identifier.fromNamespaceAndPath("structurize", "pipeline/entity_icon")) + .withVertexShader("core/position_tex") + .withFragmentShader("core/position_tex") + .withColorTargetState(new ColorTargetState(BlendFunction.TRANSLUCENT)) + .withVertexBinding(0, DefaultVertexFormat.POSITION_TEX) + .withPrimitiveTopology(com.mojang.blaze3d.PrimitiveTopology.QUADS) + .withDepthStencilState(new DepthStencilState(CompareOp.ALWAYS_PASS, false)) + .build(); + return RenderType.create( + "structurize:entity_icon", + RenderSetup.builder(pipeline).withTexture("Sampler0", texture).createRenderSetup()); + }); + + private static RenderType positionColor(final String name, + final com.mojang.blaze3d.PrimitiveTopology mode, + final BlendFunction blendFunction, + final CompareOp depthTest, + final boolean writeDepth, + final boolean cull, + final int bufferSize) + { + final RenderPipeline pipeline = RenderPipeline.builder(RenderPipelines.GLOBALS_SNIPPET) + .withLocation(Identifier.fromNamespaceAndPath("structurize", "pipeline/" + name.substring(name.indexOf(':') + 1))) + .withVertexShader("core/position_color") + .withFragmentShader("core/position_color") + .withColorTargetState(new ColorTargetState(blendFunction)) + .withCull(cull) + .withVertexBinding(0, DefaultVertexFormat.POSITION_COLOR) + .withPrimitiveTopology(mode) + .withDepthStencilState(new DepthStencilState(depthTest, writeDepth)) + .build(); + return RenderType.create(name, RenderSetup.builder(pipeline).createRenderSetup()); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/util/WorldRenderMacros.java b/src/main/java/com/ldtteam/structurize/client/rendertask/util/WorldRenderMacros.java new file mode 100644 index 0000000000..af4c1e94b8 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/util/WorldRenderMacros.java @@ -0,0 +1,1133 @@ +package com.ldtteam.structurize.client.rendertask.util; + +import com.ldtteam.blockui.UiRenderMacros; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import com.ldtteam.structurize.client.rendertask.util.BufferSourceCompat; +import net.minecraft.gizmos.Gizmos; +import net.minecraft.world.phys.Vec3; +import net.minecraft.gizmos.TextGizmo; + +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.ARGB; +import net.minecraft.util.Util; +import net.minecraft.world.phys.AABB; +import org.joml.Matrix4f; + +import java.util.LinkedList; +import java.util.List; + +public class WorldRenderMacros extends UiRenderMacros +{ + private static final int MAX_DEBUG_TEXT_RENDER_DIST_SQUARED = 8 * 8 * 16; + public static final RenderType LINES = RenderTypes.LINES; + public static final RenderType LINES_WITH_WIDTH = RenderTypes.LINES_WITH_WIDTH; + public static final RenderType GLINT_LINES = RenderTypes.GLINT_LINES; + public static final RenderType GLINT_LINES_WITH_WIDTH = RenderTypes.GLINT_LINES_WITH_WIDTH; + public static final RenderType COLORED_TRIANGLES = RenderTypes.COLORED_TRIANGLES; + public static final RenderType COLORED_TRIANGLES_NC_ND = RenderTypes.COLORED_TRIANGLES_NC_ND; + + private static final LinkedList buffers = new LinkedList<>(); + /** + * Always use {@link #getBufferSource} when actually using the buffer source + */ + private static BufferSourceCompat bufferSource; + + /** + * Put type at the first position. + * + * @param bufferType type to put in + */ + public static void putBufferHead(final RenderType bufferType) + { + buffers.addFirst(bufferType); + bufferSource = null; + } + + /** + * Put type at the last position. + * + * @param bufferType type to put in + */ + public static void putBufferTail(final RenderType bufferType) + { + buffers.addLast(bufferType); + bufferSource = null; + } + + /** + * Put type before the given buffer or if not found then at first position. + * + * @param bufferType type to put in + * @param putBefore search for type to put before + */ + public static void putBufferBefore(final RenderType bufferType, final RenderType putBefore) + { + buffers.add(Math.max(0, buffers.indexOf(putBefore)), bufferType); + bufferSource = null; + } + + /** + * Put type after the given buffer or if not found then at last position. + * + * @param bufferType type to put in + * @param putAfter search for type to put after + */ + public static void putBufferAfter(final RenderType bufferType, final RenderType putAfter) + { + final int index = buffers.indexOf(putAfter); + if (index == -1) + { + buffers.add(bufferType); + } + else + { + buffers.add(index + 1, bufferType); + } + bufferSource = null; + } + + static + { + putBufferTail(WorldRenderMacros.COLORED_TRIANGLES); + putBufferTail(WorldRenderMacros.LINES); + putBufferTail(WorldRenderMacros.LINES_WITH_WIDTH); + putBufferTail(WorldRenderMacros.GLINT_LINES); + putBufferTail(WorldRenderMacros.GLINT_LINES_WITH_WIDTH); + putBufferTail(WorldRenderMacros.COLORED_TRIANGLES_NC_ND); + } + + public static BufferSourceCompat getBufferSource() + { + if (bufferSource == null) + { + bufferSource = new BufferSourceCompat(); + } + return bufferSource; + } + + /** + * Render a black box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderBlackLineBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final float lineWidth) + { + renderLineBox(buffer.getBuffer(LINES_WITH_WIDTH), ps, posA, posB, 0x00, 0x00, 0x00, 0xff, lineWidth); + } + + /** + * Render a red glint box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderRedGlintLineBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final float lineWidth) + { + renderLineBox(buffer.getBuffer(GLINT_LINES_WITH_WIDTH), ps, posA, posB, 0xff, 0x0, 0x0, 0xff, lineWidth); + } + + /** + * Render a white box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderWhiteLineBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final float lineWidth) + { + renderLineBox(buffer.getBuffer(LINES_WITH_WIDTH), ps, posA, posB, 0xff, 0xff, 0xff, 0xff, lineWidth); + } + + /** + * Render a colored box around from aabb + * + * @param aabb the box + */ + public static void renderLineAABB(final VertexConsumer buffer, + final PoseStack ps, + final AABB aabb, + final int argbColor, + final float lineWidth) + { + renderLineAABB(buffer, + ps, + aabb, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + lineWidth); + } + + /** + * Render a colored box around from aabb + * + * @param aabb the box + */ + public static void renderLineAABB(final VertexConsumer buffer, + final PoseStack ps, + final AABB aabb, + final int red, + final int green, + final int blue, + final int alpha, + final float lineWidth) + { + renderLineBox(buffer, + ps, + (float) aabb.minX, + (float) aabb.minY, + (float) aabb.minZ, + (float) aabb.maxX, + (float) aabb.maxY, + (float) aabb.maxZ, + red, + green, + blue, + alpha, + lineWidth); + } + + /** + * Render a colored box around position + * + * @param pos The Position + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos pos, + final int argbColor, + final float lineWidth) + { + renderLineBox(buffer, + ps, + pos, + pos, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + lineWidth); + } + + /** + * Render a colored box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int argbColor, + final float lineWidth) + { + renderLineBox(buffer, + ps, + posA, + posB, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + lineWidth); + } + + /** + * Render a box around two positions + * + * @param posA First position. + * @param posB Second position. + * @param red Red component. + * @param green Green component. + * @param blue Blue component. + * @param alpha Alpha component. + * @param lineWidth Line width. + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int red, + final int green, + final int blue, + final int alpha, + final float lineWidth) + { + renderLineBox(buffer, + ps, + Math.min(posA.getX(), posB.getX()), + Math.min(posA.getY(), posB.getY()), + Math.min(posA.getZ(), posB.getZ()), + Math.max(posA.getX(), posB.getX()) + 1, + Math.max(posA.getY(), posB.getY()) + 1, + Math.max(posA.getZ(), posB.getZ()) + 1, + red, + green, + blue, + alpha, + lineWidth); + } + + /** + * Render a box around two positions + * + * @param minX Minimum X coordinate. + * @param minY Minimum Y coordinate. + * @param minZ Minimum Z coordinate. + * @param maxX Maximum X coordinate. + * @param maxY Maximum Y coordinate. + * @param maxZ Maximum Z coordinate. + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + float minX, + float minY, + float minZ, + float maxX, + float maxY, + float maxZ, + final int red, + final int green, + final int blue, + final int alpha, + final float lineWidth) + { + if (alpha == 0) + { + return; + } + + final float halfLine = lineWidth / 2.0f; + minX -= halfLine; + minY -= halfLine; + minZ -= halfLine; + final float minX2 = minX + lineWidth; + final float minY2 = minY + lineWidth; + final float minZ2 = minZ + lineWidth; + + maxX += halfLine; + maxY += halfLine; + maxZ += halfLine; + final float maxX2 = maxX - lineWidth; + final float maxY2 = maxY - lineWidth; + final float maxZ2 = maxZ - lineWidth; + + final Matrix4f m = ps.last().pose(); + populateRenderLineBox( + minX, + minY, + minZ, + minX2, + minY2, + minZ2, + maxX, + maxY, + maxZ, + maxX2, + maxY2, + maxZ2, + (alpha << 24) | (red << 16) | (green << 8) | blue, + m, + buffer); + } + + // TODO: ebo this, does vanilla have any ebo things? + public static void populateRenderLineBox(final float minX, + final float minY, + final float minZ, + final float minX2, + final float minY2, + final float minZ2, + final float maxX, + final float maxY, + final float maxZ, + final float maxX2, + final float maxY2, + final float maxZ2, + final int argbColor, + final Matrix4f m, + final VertexConsumer buf) + { + // z plane + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + + // + + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + // + + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + // x plane + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + + // + + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + + // y plane + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + } + + public static void renderBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int argbColor) + { + renderBox(buffer.getBuffer(COLORED_TRIANGLES), + ps, + posA, + posB, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff); + } + + public static void renderBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int red, + final int green, + final int blue, + final int alpha) + { + if (alpha == 0) + { + return; + } + + final float minX = Math.min(posA.getX(), posB.getX()); + final float minY = Math.min(posA.getY(), posB.getY()); + final float minZ = Math.min(posA.getZ(), posB.getZ()); + + final float maxX = Math.max(posA.getX(), posB.getX()) + 1; + final float maxY = Math.max(posA.getY(), posB.getY()) + 1; + final float maxZ = Math.max(posA.getZ(), posB.getZ()) + 1; + + final Matrix4f m = ps.last().pose(); + final int argbColor = (alpha << 24) | (red << 16) | (green << 8) | blue; + + populateCuboid(minX, minY, minZ, maxX, maxY, maxZ, argbColor, m, buffer); + } + + public static void populateCuboid(final float minX, + final float minY, + final float minZ, + final float maxX, + final float maxY, + final float maxZ, + final int argbColor, + final Matrix4f m, + final VertexConsumer buf) + { + // z plane + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + + // y plane + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + // x plane + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + } + + public static void renderFillRectangle(final BufferSourceCompat buffer, + final PoseStack ps, + final int x, + final int y, + final int z, + final int w, + final int h, + final int argbColor) + { + populateRectangle(x, + y, + z, + w, + h, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + buffer.getBuffer(COLORED_TRIANGLES_NC_ND), + ps.last().pose()); + } + + public static void populateRectangle(final int x, + final int y, + final int z, + final int w, + final int h, + final int red, + final int green, + final int blue, + final int alpha, + final VertexConsumer buffer, + final Matrix4f m) + { + if (alpha == 0) + { + return; + } + + final int argbColor = (alpha << 24) | (red << 16) | (green << 8) | blue; + + buffer.addVertex(m, x, y, z).setColor(argbColor); + buffer.addVertex(m, x, y + h, z).setColor(argbColor); + buffer.addVertex(m, x + w, y + h, z).setColor(argbColor); + + buffer.addVertex(m, x, y, z).setColor(argbColor); + buffer.addVertex(m, x + w, y + h, z).setColor(argbColor); + buffer.addVertex(m, x + w, y, z).setColor(argbColor); + } + + /** + * Renders the given list of strings, 3 elements a row. + * + * @param pos position to render at + * @param text text list + * @param matrixStack stack to use + * @param buffer render buffer + * @param forceWhite force white for no depth rendering + * @param mergeEveryXListElements merge every X elements of text list using a tostring call + */ + public static void renderDebugText(final BlockPos pos, + final List text, + final PoseStack matrixStack, + final boolean forceWhite, + final int mergeEveryXListElements, + final BufferSourceCompat buffer) + { + renderDebugText(pos, pos, text, matrixStack, forceWhite, mergeEveryXListElements, buffer); + } + + /** + * Renders the given list of strings, 3 elements a row. + * + * @param renderPos position to render at + * @param worldPos (logic) position in world + * @param text text list + * @param matrixStack stack to use + * @param buffer render buffer + * @param forceWhite force white for no depth rendering + * @param mergeEveryXListElements merge every X elements of text list using a tostring call + */ + @SuppressWarnings("resource") + public static void renderDebugText(final BlockPos renderPos, + final BlockPos worldPos, + final List text, + final PoseStack matrixStack, + final boolean forceWhite, + final int mergeEveryXListElements, + final BufferSourceCompat buffer) + { + if (mergeEveryXListElements < 1) + { + throw new IllegalArgumentException("mergeEveryXListElements is less than 1"); + } + + final EntityRenderDispatcher erm = Minecraft.getInstance().getEntityRenderDispatcher(); + final int cap = text.size(); + if (cap > 0 && Minecraft.getInstance().gameRenderer.mainCamera().position().distanceToSqr(worldPos.getX(), worldPos.getY(), worldPos.getZ()) <= MAX_DEBUG_TEXT_RENDER_DIST_SQUARED) + { + final Font fontrenderer = Minecraft.getInstance().font; + + matrixStack.pushPose(); + matrixStack.translate(renderPos.getX() + 0.5d, renderPos.getY() + 0.6d, renderPos.getZ() + 0.5d); + matrixStack.mulPose(erm.camera.rotation()); + matrixStack.scale(-0.014f, -0.014f, 0.014f); + + final float backgroundTextOpacity = 0f; + final int alphaMask = (int) (backgroundTextOpacity * 255.0F) << 24; + + final Matrix4f rawPosMatrix = matrixStack.last().pose(); + + for (int i = 0; i < cap; i += mergeEveryXListElements) + { + final MutableComponent renderText = Component.literal( + mergeEveryXListElements == 1 ? text.get(i) : text.subList(i, Math.min(i + mergeEveryXListElements, cap)).toString()); + final float textCenterShift = (float) (-fontrenderer.width(renderText) / 2); + + final Vec3 textWorldPos = new Vec3( + renderPos.getX() + 0.5d, + renderPos.getY() + 0.6d + i * (fontrenderer.lineHeight + 1) * 0.014f, + renderPos.getZ() + 0.5d); + + Gizmos.billboardText(renderText.getString(), + textWorldPos, + TextGizmo.Style.forColorAndCentered(forceWhite ? 0xffffffff : 0x20ffffff)); + if (!forceWhite) + { + Gizmos.billboardText(renderText.getString(), + textWorldPos, + TextGizmo.Style.forColorAndCentered(0xffffffff)); + } + } + + matrixStack.popPose(); + } + } + + /** + * Render a wireframe box. + * + * @param poseStack pose stack + * @param bufferSource buffer source + * @param bounds bounding box to draw + * @param width line width + * @param color line color (ARGB) + * @param showThroughBlocks true to render through existing blocks, false to only render in air + */ + public static void renderLineBox( + final PoseStack poseStack, final BufferSourceCompat bufferSource, + final AABB bounds, final float width, final int color, final boolean showThroughBlocks) + { + final float halfLine = width / 2.0f; + final float minX = (float) (bounds.minX - halfLine); + final float minY = (float) (bounds.minY - halfLine); + final float minZ = (float) (bounds.minZ - halfLine); + final float minX2 = minX + width; + final float minY2 = minY + width; + final float minZ2 = minZ + width; + + final float maxX = (float) (bounds.maxX + halfLine); + final float maxY = (float) (bounds.maxY + halfLine); + final float maxZ = (float) (bounds.maxZ + halfLine); + final float maxX2 = maxX - width; + final float maxY2 = maxY - width; + final float maxZ2 = maxZ - width; + + final int red = ARGB.red(color); + final int green = ARGB.green(color); + final int blue = ARGB.blue(color); + final int alpha = ARGB.alpha(color); + + if (showThroughBlocks) + { + renderLineBox(poseStack, bufferSource.getBuffer(RenderTypes.LINES_INSIDE_BLOCKS), + minX, minY, minZ, minX2, minY2, minZ2, maxX, maxY, maxZ, maxX2, maxY2, maxZ2, + red / 2, green / 2, blue / 2, alpha / 2); + } + + renderLineBox(poseStack, bufferSource.getBuffer(RenderTypes.LINES_OUTSIDE_BLOCKS), + minX, minY, minZ, minX2, minY2, minZ2, maxX, maxY, maxZ, maxX2, maxY2, maxZ2, + red, green, blue, alpha); + } + + /** + * Call after a series of {@link #renderLineBox(PoseStack, BufferSourceCompat, AABB, float, int, boolean)} + * + * @param bufferSource buffer source + */ + public static void endRenderLineBox(final BufferSourceCompat bufferSource) + { + bufferSource.endBatch(); + } + + /** + * Render a wireframe box. + * + * @param poseStack pose stack + * @param buffer buffer + * @param minX min X + * @param minY min Y + * @param minZ min Z + * @param minX2 min X + width + * @param minY2 min Y + width + * @param minZ2 min Z + width + * @param maxX max X + * @param maxY max Y + * @param maxZ max Z + * @param maxX2 max X - width + * @param maxY2 max Y - width + * @param maxZ2 max Z - width + * @param red red + * @param green green + * @param blue blue + * @param alpha alpha + */ + private static void renderLineBox( + final PoseStack poseStack, final VertexConsumer buffer, + final float minX, final float minY, final float minZ, + final float minX2, final float minY2, final float minZ2, + final float maxX, final float maxY, final float maxZ, + final float maxX2, final float maxY2, final float maxZ2, + final int red, final int green, final int blue, final int alpha) + { + final int argbColor = (alpha << 24) | (red << 16) | (green << 8) | blue; + WorldRenderMacros.populateRenderLineBox( + minX, + minY, + minZ, + minX2, + minY2, + minZ2, + maxX, + maxY, + maxZ, + maxX2, + maxY2, + maxZ2, + argbColor, + poseStack.last().pose(), + buffer); + } +} diff --git a/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java b/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java index dd5afefab0..628ab23ad0 100644 --- a/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java @@ -4,8 +4,10 @@ import java.util.List; import java.util.Optional; import java.util.function.Supplier; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.util.LanguageHandler; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.LiteralMessage; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; @@ -13,8 +15,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands.CommandSelection; -import net.minecraft.network.chat.Component; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; /** * Interface for all commands @@ -72,7 +73,7 @@ protected static RequiredArgumentBuilder newArgument( */ public static void throwSyntaxException(final String key) throws CommandSyntaxException { - throw new CommandSyntaxException(new StructurizeCommandExceptionType(), Component.translatable(key)); + throw new CommandSyntaxException(new StructurizeCommandExceptionType(), new LiteralMessage(LanguageHandler.translateKey(key))); } /** @@ -83,7 +84,8 @@ public static void throwSyntaxException(final String key) throws CommandSyntaxEx */ public static void throwSyntaxException(final String key, final Object... format) throws CommandSyntaxException { - throw new CommandSyntaxException(new StructurizeCommandExceptionType(), Component.translatable(key, format)); + throw new CommandSyntaxException(new StructurizeCommandExceptionType(), + new LiteralMessage(LanguageHandler.translateKeyWithFormat(key, format))); } /** diff --git a/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java b/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java index 536be39246..892e321027 100644 --- a/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java @@ -8,9 +8,11 @@ import com.ldtteam.structurize.placement.structure.CreativeStructureHandler; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; -import com.mojang.authlib.GameProfile; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; +import net.minecraft.server.players.NameAndId; import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; @@ -23,8 +25,9 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.server.command.EnumArgument; import org.jetbrains.annotations.Nullable; /** @@ -85,14 +88,19 @@ public class PasteCommand extends AbstractCommand /** * The rotation command argument. */ - private static final String ROT_MIR = "rotation_mirror"; + private static final String ROTATION = "rotation"; + + /** + * The mirror command argument. + */ + private static final String MIRROR = "mirror"; /** * The pretty command argument. */ private static final String PRETTY = "pretty"; - private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final RotationMirror rotMir, final boolean pretty, final Player player) throws CommandSyntaxException + private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final Rotation rotation, final boolean mirrored, final boolean pretty, final Player player) throws CommandSyntaxException { @Nullable final Level world = source.getLevel(); if (source.getEntity() instanceof Player && !source.getPlayerOrException().isCreative()) @@ -107,6 +115,8 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } + final Mirror mirror = mirrored ? Mirror.FRONT_BACK : Mirror.NONE; + final String[] split = tempPath.split("\\."); final StringBuilder builder = new StringBuilder(); for (final String part : split) @@ -137,7 +147,7 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } - final Blueprint blueprint = StructurePacks.getBlueprint(packName, path + ".blueprint", true, source.registryAccess()); + final Blueprint blueprint = StructurePacks.getBlueprint(packName, path + ".blueprint", true); if (blueprint == null) { source.sendFailure(Component.translatable(NO_BLUEPRINT_MESSAGE)); @@ -145,20 +155,27 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, } final BlockState anchor = blueprint.getBlockState(blueprint.getPrimaryBlockOffset()); - blueprint.setRotationMirror(rotMir, world); + blueprint.setRotationMirror(RotationMirror.of(rotation, mirror), world); final IStructureHandler structure; - if (anchor.getBlock() instanceof final ISpecialCreativeHandlerAnchorBlock specialAnchor) + if (anchor.getBlock() instanceof ISpecialCreativeHandlerAnchorBlock) { - if (!specialAnchor.setup((ServerPlayer) player, world, pos, blueprint, rotMir, pretty, packName, path)) + if (!((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).setup((ServerPlayer) player, world, pos, blueprint, new PlacementSettings(mirror, rotation), + pretty, packName, path)) { return 0; } - structure = specialAnchor.getStructureHandler(world, pos, blueprint, rotMir, pretty); + structure = + ((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).getStructureHandler(world, pos, blueprint, new PlacementSettings(mirror, rotation), + pretty); } else { - structure = new CreativeStructureHandler(world, pos, blueprint, rotMir, pretty); + structure = new CreativeStructureHandler(world, + pos, + blueprint, + new PlacementSettings(mirror, rotation), + pretty); } final StructurePlacer instantPlacer = new StructurePlacer(structure); @@ -174,7 +191,17 @@ private static int onExecute(final CommandContext context) t final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - return execute(context.getSource(), pos, packName, path, RotationMirror.NONE, true, context.getSource().getPlayer()); + return execute(context.getSource(), pos, packName, path, Rotation.NONE, false, true, context.getSource().getPlayer()); + } + + private static int onExecuteWithRotation(final CommandContext context) throws CommandSyntaxException + { + final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); + final String packName = StringArgumentType.getString(context, PACK_NAME); + final String path = StringArgumentType.getString(context, FILE_PATH); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + + return execute(context.getSource(), pos, packName, path, rotation, false, true, context.getSource().getPlayer()); } private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -182,9 +209,10 @@ private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -192,10 +220,11 @@ private static int onExecuteWithFull(final CommandContext co final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - final RotationMirror rotMir = context.getArgument(ROT_MIR, RotationMirror.class); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + final boolean mirror = BoolArgumentType.getBool(context, MIRROR); final boolean pretty = BoolArgumentType.getBool(context, PRETTY); - return execute(context.getSource(), pos, packName, path, rotMir, pretty, context.getSource().getPlayer()); + return execute(context.getSource(), pos, packName, path, rotation, mirror, pretty, context.getSource().getPlayer()); } private static int onExecuteWithFullAndPlayer(final CommandContext context) throws CommandSyntaxException @@ -203,9 +232,10 @@ private static int onExecuteWithFullAndPlayer(final CommandContext build() @@ -223,11 +253,13 @@ protected static LiteralArgumentBuilder build() .then(newArgument(PACK_NAME, StringArgumentType.string()) .then(newArgument(FILE_PATH, StringArgumentType.string()) .executes(PasteCommand::onExecute) - .then(newArgument(ROT_MIR, EnumArgument.enumArgument(RotationMirror.class)) - .executes(PasteCommand::onExecuteWithRotationAndMirror) + .then(newArgument(ROTATION, IntegerArgumentType.integer(0, 3)) + .executes(PasteCommand::onExecuteWithRotation) + .then(newArgument(MIRROR, BoolArgumentType.bool()) + .executes(PasteCommand::onExecuteWithRotationAndMirror) .then(newArgument(PRETTY, BoolArgumentType.bool()) .executes(PasteCommand::onExecuteWithFull) .then(newArgument(PLAYER_NAME, GameProfileArgument.gameProfile()) - .executes(PasteCommand::onExecuteWithFullAndPlayer))))))); + .executes(PasteCommand::onExecuteWithFullAndPlayer)))))))); } } diff --git a/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java b/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java index 956b38bca9..1eb81bd06d 100644 --- a/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java @@ -10,7 +10,8 @@ import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.storage.ServerFutureProcessor; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; @@ -18,14 +19,16 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.arguments.GameProfileArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.server.command.EnumArgument; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -63,6 +66,11 @@ public class PasteFolderCommand extends AbstractCommand */ private static final String NO_PACK_MESSAGE = "com.structurize.command.paste.no.pack"; + /** + * The player name command argument. + */ + private static final String PLAYER_NAME = "player"; + /** * Position command argument. */ @@ -81,7 +89,12 @@ public class PasteFolderCommand extends AbstractCommand /** * The rotation command argument. */ - private static final String ROT_MIR = "rotation_mirror"; + private static final String ROTATION = "rotation"; + + /** + * The mirror command argument. + */ + private static final String MIRROR = "mirror"; /** * The pretty command argument. @@ -93,7 +106,7 @@ public class PasteFolderCommand extends AbstractCommand */ private static final String PLOT_SIZE = "plotSize"; - private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final RotationMirror rotMir, final boolean pretty, final int plotSize) throws CommandSyntaxException + private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final Rotation rotation, final boolean mirrored, final boolean pretty, final int plotSize) throws CommandSyntaxException { @Nullable final Level world = source.getLevel(); if (source.getEntity() instanceof Player && !source.getPlayerOrException().isCreative()) @@ -113,6 +126,8 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } + final Mirror mirror = mirrored ? Mirror.FRONT_BACK : Mirror.NONE; + final String[] split = tempPath.split("\\."); final StringBuilder builder = new StringBuilder(); for (final String part : split) @@ -143,7 +158,7 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } - ServerFutureProcessor.queueBlueprintList(new ServerFutureProcessor.BlueprintListProcessingData(StructurePacks.getBlueprintsFuture(packName, path, source.registryAccess()), world, (list) -> { + ServerFutureProcessor.queueBlueprintList(new ServerFutureProcessor.BlueprintListProcessingData(StructurePacks.getBlueprintsFuture(packName, path), world, (list) -> { final Map> blueprintMapping = new LinkedHashMap<>(); for (Blueprint blueprint : list) @@ -174,21 +189,28 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, for (final Blueprint blueprint : perTypeList) { final BlockState anchor = blueprint.getBlockState(blueprint.getPrimaryBlockOffset()); - blueprint.setRotationMirror(rotMir, world); + blueprint.setRotationMirror(RotationMirror.of(rotation, mirror), world); final BlockPos placementPos = pos.offset(xOffset, 0, zOffset).offset(blueprint.getPrimaryBlockOffset()); final IStructureHandler structure; - if (anchor.getBlock() instanceof final ISpecialCreativeHandlerAnchorBlock specialAnchor) + if (anchor.getBlock() instanceof ISpecialCreativeHandlerAnchorBlock) { - if (!specialAnchor.setup((ServerPlayer) player, world, placementPos, blueprint, rotMir, pretty, packName, path)) + if (!((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).setup((ServerPlayer) player, world, placementPos, blueprint, new PlacementSettings(mirror, rotation), + pretty, packName, path)) { return; } - structure = specialAnchor.getStructureHandler(world, placementPos, blueprint, rotMir, pretty); + structure = + ((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).getStructureHandler(world, placementPos, blueprint, new PlacementSettings(mirror, rotation), + pretty); } else { - structure = new CreativeStructureHandler(world, placementPos, blueprint, rotMir, pretty); + structure = new CreativeStructureHandler(world, + placementPos, + blueprint, + new PlacementSettings(mirror, rotation), + pretty); } final StructurePlacer instantPlacer = new StructurePlacer(structure); @@ -210,7 +232,17 @@ private static int onExecute(final CommandContext context) t final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - return execute(context.getSource(), pos, packName, path, RotationMirror.NONE, true, 34); + return execute(context.getSource(), pos, packName, path, Rotation.NONE, false, true, 34); + } + + private static int onExecuteWithRotation(final CommandContext context) throws CommandSyntaxException + { + final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); + final String packName = StringArgumentType.getString(context, PACK_NAME); + final String path = StringArgumentType.getString(context, FILE_PATH); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + + return execute(context.getSource(), pos, packName, path, rotation, false, true, 34); } private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -218,9 +250,10 @@ private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -228,10 +261,11 @@ private static int onExecuteWithPretty(final CommandContext final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - final RotationMirror rotMir = context.getArgument(ROT_MIR, RotationMirror.class); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + final boolean mirror = BoolArgumentType.getBool(context, MIRROR); final boolean pretty = BoolArgumentType.getBool(context, PRETTY); - return execute(context.getSource(), pos, packName, path, rotMir, pretty, 34); + return execute(context.getSource(), pos, packName, path, rotation, mirror, pretty, 34); } private static int onExecuteWithFull(final CommandContext context) throws CommandSyntaxException @@ -239,11 +273,12 @@ private static int onExecuteWithFull(final CommandContext co final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - final RotationMirror rotMir = context.getArgument(ROT_MIR, RotationMirror.class); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + final boolean mirror = BoolArgumentType.getBool(context, MIRROR); final boolean pretty = BoolArgumentType.getBool(context, PRETTY); final int plotSize = IntegerArgumentType.getInteger(context, PLOT_SIZE); - return execute(context.getSource(), pos, packName, path, rotMir, pretty, plotSize); + return execute(context.getSource(), pos, packName, path, rotation, mirror, pretty, plotSize); } protected static LiteralArgumentBuilder build() @@ -253,11 +288,14 @@ protected static LiteralArgumentBuilder build() .then(newArgument(PACK_NAME, StringArgumentType.string()) .then(newArgument(FILE_PATH, StringArgumentType.string()) .executes(PasteFolderCommand::onExecute) - .then(newArgument(ROT_MIR, EnumArgument.enumArgument(RotationMirror.class)) + .then(newArgument(ROTATION, IntegerArgumentType.integer(0, 3)) + .executes(PasteFolderCommand::onExecuteWithRotation) + .then(newArgument(PLAYER_NAME, GameProfileArgument.gameProfile()) + .then(newArgument(MIRROR, BoolArgumentType.bool()) .executes(PasteFolderCommand::onExecuteWithRotationAndMirror) .then(newArgument(PRETTY, BoolArgumentType.bool()) .executes(PasteFolderCommand::onExecuteWithPretty) .then(newArgument(PLOT_SIZE, IntegerArgumentType.integer(16, 128)) - .executes(PasteFolderCommand::onExecuteWithFull))))))); + .executes(PasteFolderCommand::onExecuteWithFull))))))))); } } diff --git a/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java b/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java index 96c2b8c7ce..d18b9ce3da 100644 --- a/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java @@ -1,29 +1,29 @@ package com.ldtteam.structurize.commands; -import com.ldtteam.structurize.api.BlockPosUtil; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; import com.ldtteam.structurize.items.ItemScanTool; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; import com.ldtteam.structurize.util.ScanToolData; -import com.mojang.authlib.GameProfile; +import net.minecraft.server.players.NameAndId; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.commands.arguments.GameProfileArgument; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.StringTag; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Player; -import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Optional; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * Command for opening WindowScanTool or scanning a structure into a file @@ -75,7 +75,7 @@ public class ScanCommand extends AbstractCommand */ public static final String ANCHOR_POS = "anchor_pos"; - private static int execute(final CommandSourceStack source, final BlockPos from, final BlockPos to, final Optional anchorPos, final GameProfile profile, final String name) throws CommandSyntaxException + private static int execute(final CommandSourceStack source, final BlockPos from, final BlockPos to, final Optional anchorPos, final NameAndId profile, final String name) throws CommandSyntaxException { @Nullable final Level world = source.getLevel(); if (source.getEntity() instanceof Player && !source.getPlayerOrException().isCreative()) @@ -87,10 +87,10 @@ private static int execute(final CommandSourceStack source, final BlockPos from, final Player player; if (profile != null && world.getServer() != null) { - player = world.getServer().getPlayerList().getPlayer(profile.getId()); + player = world.getServer().getPlayerList().getPlayer(profile.id()); if (player == null) { - source.sendFailure(Component.translatable(PLAYER_NOT_FOUND, profile.getName())); + source.sendFailure(Component.translatable(PLAYER_NOT_FOUND, profile.name())); return 0; } } @@ -128,7 +128,7 @@ private static int onExecuteWithPlayerName(final CommandContext build() @NotNull public static String format(@NotNull final ScanToolData.Slot slot) { - final String name = slot.name().chars().anyMatch(c -> !StringReader.isAllowedInUnquotedString((char)c)) - ? StringTag.quoteAndEscape(slot.name()) : slot.name(); + final String name = slot.getName().chars().anyMatch(c -> !StringReader.isAllowedInUnquotedString((char)c)) + ? StringTag.quoteAndEscape(slot.getName()) : slot.getName(); final StringBuilder builder = new StringBuilder(); builder.append(String.format("/%s %s %s %s @p %s", MOD_ID, NAME, - BlockPosUtil.format(slot.box().pos1()), - BlockPosUtil.format(slot.box().pos2()), + BlockPosUtil.format(slot.getBox().getPos1()), + BlockPosUtil.format(slot.getBox().getPos2()), name)); - if (slot.box().anchor().isPresent() && BlockPosUtil.isInbetween(slot.box().anchor().get(), slot.box().pos1(), slot.box().pos2())) + if (slot.getBox().getAnchor().isPresent() && BlockPosUtil.isInbetween(slot.getBox().getAnchor().get(), slot.getBox().getPos1(), slot.getBox().getPos2())) { builder.append(' '); - builder.append(BlockPosUtil.format(slot.box().anchor().get())); + builder.append(BlockPosUtil.format(slot.getBox().getAnchor().get())); } return builder.toString(); } diff --git a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java index 013c8a58a0..28dd7c383f 100644 --- a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java @@ -13,8 +13,8 @@ import net.minecraft.client.Minecraft; import net.minecraft.commands.CommandSourceStack; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtAccounter; import net.minecraft.network.chat.Component; import java.io.BufferedOutputStream; @@ -77,12 +77,12 @@ public static void fixBlueprints(final StructurePackMeta packMeta, final Command final CompoundTag nbt = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap()); inputStream.close(); - int currentDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); - final int oldDataVersion = nbt.contains("mcversion") ? nbt.getInt("mcversion") : DEFAULT_FIXER_IF_NOT_FOUND; + int currentDataVersion = SharedConstants.getCurrentVersion().dataVersion().version(); + final int oldDataVersion = nbt.contains("mcversion") ? nbt.getIntOr("mcversion", 0) : DEFAULT_FIXER_IF_NOT_FOUND; if (oldDataVersion != currentDataVersion) { - final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt, source.registryAccess()); + final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt); if (blueprint == null) { return; diff --git a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java index b13cf81eda..4fb0b58d44 100644 --- a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.commands; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.DataFixerUtils; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -9,7 +9,6 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.*; import net.minecraft.util.datafix.fixes.References; @@ -25,7 +24,7 @@ import java.util.*; import java.util.stream.Stream; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; import static com.ldtteam.structurize.blueprints.v1.BlueprintUtil.*; /** @@ -56,7 +55,7 @@ private static int onExecute(final CommandContext command) t { try (final Stream paths = Files.list(gameFolder.resolve("input"))) { - paths.forEach(element -> update(element, gameFolder.resolve("input"), gameFolder.resolve("output"), command.getSource().registryAccess())); + paths.forEach(element -> update(element, gameFolder.resolve("input"), gameFolder.resolve("output"))); } } catch (IOException e) @@ -68,7 +67,7 @@ private static int onExecute(final CommandContext command) t return 1; } - private static void update(final Path input, final Path globalInputFolder, final Path globalOutputFolder, final HolderLookup.Provider provider) + private static void update(final Path input, final Path globalInputFolder, final Path globalOutputFolder) { if (Files.isDirectory(input)) { @@ -76,7 +75,7 @@ private static void update(final Path input, final Path globalInputFolder, final { try (final Stream paths = Files.list(input)) { - paths.forEach(element -> update(element, globalInputFolder, globalOutputFolder, provider)); + paths.forEach(element -> update(element, globalInputFolder, globalOutputFolder)); } } catch (IOException e) @@ -94,7 +93,7 @@ private static void update(final Path input, final Path globalInputFolder, final if (input.toString().endsWith(".blueprint")) { - final CompoundTag bluePrintCompound = writeBlueprintToNBT(fixBluePrints(input, provider)); + final CompoundTag bluePrintCompound = writeBlueprintToNBT(fixBluePrints(input)); try (final OutputStream outputstream = new BufferedOutputStream(Files.newOutputStream(output))) { NbtIo.writeCompressed(bluePrintCompound, outputstream); @@ -113,13 +112,17 @@ private static void update(final Path input, final Path globalInputFolder, final return; } - final ListTag blocks = blueprint.getList("blocks", Tag.TAG_COMPOUND); - final ListTag pallete = blueprint.getList("palette", Tag.TAG_COMPOUND); + final ListTag blocks = blueprint.getListOrEmpty("blocks"); + final ListTag pallete = blueprint.getListOrEmpty("palette"); final CompoundTag bluePrintCompound = new CompoundTag(); - final ListTag list = blueprint.getList("size", Tag.TAG_INT); - final int[] size = new int[] {list.getInt(0), list.getInt(1), list.getInt(2)}; + final ListTag list = blueprint.getListOrEmpty("size"); + final int[] size = new int[] { + list.getInt(0).orElse(0), + list.getInt(1).orElse(0), + list.getInt(2).orElse(0) + }; bluePrintCompound.putShort("size_x", (short) size[0]); bluePrintCompound.putShort("size_y", (short) size[1]); bluePrintCompound.putShort("size_z", (short) size[2]); @@ -136,8 +139,8 @@ private static void update(final Path input, final Path globalInputFolder, final for (int i = 0; i < pallete.size(); i++) { - final CompoundTag blockState = pallete.getCompound(i); - final String modid = blockState.getString("Name").split(":")[0]; + final CompoundTag blockState = pallete.getCompound(i).orElse(new CompoundTag()); + final String modid = blockState.getStringOr("Name", "").split(":")[0]; mods.add(modid); } @@ -170,12 +173,12 @@ private static void update(final Path input, final Path globalInputFolder, final final ListTag tileEntities = new ListTag(); for (int i = 0; i < blocks.size(); i++) { - final CompoundTag comp = blocks.getCompound(i); + final CompoundTag comp = blocks.getCompound(i).orElse(new CompoundTag()); updatePos(pos, comp); - dataArray[pos.getY()][pos.getZ()][pos.getX()] = (short) comp.getInt("state"); + dataArray[pos.getY()][pos.getZ()][pos.getX()] = (short) comp.getIntOr("state", 0); if (comp.contains("nbt")) { - final CompoundTag te = comp.getCompound("nbt"); + final CompoundTag te = comp.getCompoundOrEmpty("nbt"); te.putShort("x", (short) pos.getX()); te.putShort("y", (short) pos.getY()); te.putShort("z", (short) pos.getZ()); @@ -192,11 +195,11 @@ private static void update(final Path input, final Path globalInputFolder, final final ListTag newEntities = new ListTag(); if (blueprint.contains("entities")) { - final ListTag entities = blueprint.getList("entities", Tag.TAG_COMPOUND); + final ListTag entities = blueprint.getListOrEmpty("entities"); for (int i = 0; i < entities.size(); i++) { - final CompoundTag entityData = entities.getCompound(i); - final CompoundTag entity = entityData.getCompound("nbt"); + final CompoundTag entityData = entities.getCompound(i).orElse(new CompoundTag()); + final CompoundTag entity = entityData.getCompoundOrEmpty("nbt"); entity.put("Pos", entityData.get("pos")); newEntities.add(entity); } @@ -218,12 +221,12 @@ private static void update(final Path input, final Path globalInputFolder, final } } - private static Blueprint fixBluePrints(final Path input, final HolderLookup.Provider provider) + private static Blueprint fixBluePrints(final Path input) { try { final CompoundTag compoundNBT = NbtIo.readCompressed(new ByteArrayInputStream(Files.readAllBytes(input)), NbtAccounter.unlimitedHeap()); - return readBlueprintFromNBT(compoundNBT, provider); + return readBlueprintFromNBT(compoundNBT); } catch (Exception e) { @@ -232,13 +235,13 @@ private static Blueprint fixBluePrints(final Path input, final HolderLookup.Prov return null; } - public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final HolderLookup.Provider provider) + public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag) { final CompoundTag tag = nbtTag; - byte version = tag.getByte("version"); + byte version = tag.getByteOr("version", (byte) 0); if (version == 1) { - short sizeX = tag.getShort("size_x"), sizeY = tag.getShort("size_y"), sizeZ = tag.getShort("size_z"); + short sizeX = tag.getShortOr("size_x", (short) 0), sizeY = tag.getShortOr("size_y", (short) 0), sizeZ = tag.getShortOr("size_z", (short) 0); // Reading required Mods List requiredMods = new ArrayList<>(); @@ -247,7 +250,7 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol short modListSize = (short) modsList.size(); for (int i = 0; i < modListSize; i++) { - requiredMods.add((modsList.get(i)).getAsString()); + requiredMods.add(modsList.getStringOr(i, "")); if (!requiredMods.get(i).equals("minecraft") && !ModList.get().getModContainerById(requiredMods.get(i)).isPresent()) { LogManager.getLogger().warn("Found missing mods for Blueprint, some blocks may be missing: " + requiredMods.get(i)); @@ -255,14 +258,14 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol } } - final int oldDataVersion = tag.contains("mcversion") ? tag.getInt("mcversion") : DEFAULT_FIXER_IF_NOT_FOUND; + final int oldDataVersion = tag.contains("mcversion") ? tag.getIntOr("mcversion", 0) : DEFAULT_FIXER_IF_NOT_FOUND; // Reading Pallete ListTag paletteTag = (ListTag) tag.get("palette"); List palette = new ArrayList<>(); // Reading Blocks - short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks"), sizeX, sizeY, sizeZ); + short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks").orElse(new int[0]), sizeX, sizeY, sizeZ); // Reading Tile Entities CompoundTag[] tes = fixTileEntities(oldDataVersion, (ListTag) tag.get("tile_entities")); @@ -284,32 +287,32 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol fixCross1343(palette, blocks, tileEntities, entities); } - final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods, provider) + final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods) .setMissingMods(missingMods.toArray(new String[0])); schem.setEntities(entities); - if (tag.getAllKeys().contains("name")) + if (tag.keySet().contains("name")) { - schem.setName(tag.getString("name")); + schem.setName(tag.getStringOr("name", "")); } - if (tag.getAllKeys().contains("architects")) + if (tag.keySet().contains("architects")) { ListTag architectsTag = (ListTag) tag.get("architects"); String[] architects = new String[architectsTag.size()]; for (int i = 0; i < architectsTag.size(); i++) { - architects[i] = architectsTag.getString(i); + architects[i] = architectsTag.getString(i).orElse(""); } schem.setArchitects(architects); } - if (tag.getAllKeys().contains(NBT_OPTIONAL_DATA_TAG)) + if (tag.keySet().contains(NBT_OPTIONAL_DATA_TAG)) { - final CompoundTag optionalTag = tag.getCompound(NBT_OPTIONAL_DATA_TAG); - if (optionalTag.getAllKeys().contains(MOD_ID)) + final CompoundTag optionalTag = tag.getCompound(NBT_OPTIONAL_DATA_TAG).orElse(new CompoundTag()); + if (optionalTag.keySet().contains(MOD_ID)) { - final CompoundTag structurizeTag = optionalTag.getCompound(MOD_ID); + final CompoundTag structurizeTag = optionalTag.getCompound(MOD_ID).orElse(new CompoundTag()); BlockPos offsetPos = BlockPosUtil.readFromNBT(structurizeTag, "primary_offset"); schem.setCachePrimaryOffset(offsetPos); } @@ -330,19 +333,19 @@ public static void fixPalette( for (short i = 0; i < paletteSize; i++) { - final CompoundTag nbt = paletteTag.getCompound(i); + final CompoundTag nbt = paletteTag.getCompound(i).orElse(new CompoundTag()); try { final CompoundTag fixedNbt = DataFixerUtils.runDataFixer(nbt, References.BLOCK_STATE, oldDataVersion); - final String name = fixedNbt.getString("Name"); + final String name = fixedNbt.getStringOr("Name", ""); if (!name.startsWith("%s:".formatted(MOD_ID))) { - final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), fixedNbt); + final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, fixedNbt); palette.add(i, state); continue; } - final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), fixedNbt); + final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, fixedNbt); palette.add(i, state); } catch (final Exception e) @@ -355,8 +358,11 @@ public static void fixPalette( private static void updatePos(final MutableBlockPos pos, final CompoundTag comp) { - final ListTag list = comp.getList("pos", Tag.TAG_INT); - pos.set(list.getInt(0), list.getInt(1), list.getInt(2)); + final ListTag list = comp.getListOrEmpty("pos"); + pos.set( + list.getInt(0).orElse(0), + list.getInt(1).orElse(0), + list.getInt(2).orElse(0)); } /** @@ -402,4 +408,4 @@ private static int[] convertBlocksToSaveData(final short[][][] multDimArray, fin } return ints; } -} \ No newline at end of file +} diff --git a/src/main/java/com/ldtteam/structurize/component/CapturedBlock.java b/src/main/java/com/ldtteam/structurize/component/CapturedBlock.java deleted file mode 100644 index 859e5abe85..0000000000 --- a/src/main/java/com/ldtteam/structurize/component/CapturedBlock.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.ldtteam.structurize.component; - -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; -import java.util.Optional; -import java.util.function.UnaryOperator; - -/** - * @param blockState state of captured block - * @param serializedBE related blockEntity data if needed - * @param itemStack itemStack representing both block and blockEntity - */ -public record CapturedBlock(BlockState blockState, Optional serializedBE, ItemStack itemStack) -{ - public static final CapturedBlock EMPTY = new CapturedBlock(Blocks.AIR.defaultBlockState(), Optional.empty(), ItemStack.EMPTY); - - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockState.CODEC.fieldOf("state").forGetter(CapturedBlock::blockState), - CompoundTag.CODEC.optionalFieldOf("entity").forGetter(CapturedBlock::serializedBE), - ItemStack.OPTIONAL_CODEC.fieldOf("item").forGetter(CapturedBlock::itemStack)) - .apply(builder, CapturedBlock::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY), - CapturedBlock::blockState, - ByteBufCodecs.OPTIONAL_COMPOUND_TAG, - CapturedBlock::serializedBE, - ItemStack.STREAM_CODEC, - CapturedBlock::itemStack, - CapturedBlock::new); - - /** - * Serializes given BE. - * - * @param blockState state of captured block - * @param blockEntity related blockEntity data if needed - * @param provider registry access - * @param itemStack itemStack representing both block and blockEntity - */ - public CapturedBlock(final BlockState blockState, - @Nullable final BlockEntity blockEntity, - final HolderLookup.Provider provider, - final ItemStack itemStack) - { - this(blockState, blockEntity == null ? Optional.empty() : Optional.of(blockEntity.saveWithId(provider)), itemStack); - } - - /** - * @param rotationMirror relative rotation and mirror - * @param level registry access - */ - public CapturedBlock applyRotationMirror(final RotationMirror rotationMirror, final Level level) - { - final BlockState rotatedState = rotationMirror.applyToBlockState(blockState); - - // No BE data: just rotate the state. - if (serializedBE.isEmpty()) - { - return new CapturedBlock(rotatedState, serializedBE, itemStack); - } - - // If the rotated state does not host a BE, drop any BE tag (prevents renderer/loader issues). - if (!rotatedState.hasBlockEntity()) - { - Log.getLogger().warn("Block {} is not empty, but has no block entity.", rotatedState); - return new CapturedBlock(rotatedState, Optional.empty(), itemStack); - } - - // Rotate/migrate the BE tag using the Blueprint rotation logic - final Blueprint blueprint = new Blueprint((short) 1, (short) 1, (short) 1, level.registryAccess()); - blueprint.addBlockState(BlockPos.ZERO, rotatedState); - blueprint.getTileEntities()[0][0][0] = serializedBE.get(); - blueprint.setCachePrimaryOffset(BlockPos.ZERO); - blueprint.setRotationMirrorRelative(rotationMirror, level); - - final CompoundTag rotatedTag = blueprint.getTileEntities()[0][0][0]; - Optional beTag = Optional.ofNullable(rotatedTag); - - // Drop invalid/empty BE tags. - if (beTag.isEmpty() || beTag.get().isEmpty() || !beTag.get().contains("id")) - { - beTag = Optional.empty(); - } - - return new CapturedBlock(rotatedState, beTag, itemStack); - } - - - public boolean hasBlockEntity() - { - return serializedBE.isPresent() && !serializedBE.get().isEmpty(); - } - - /** - * Writes this posSelection into given itemStack. - * - * @see BlockEntity#saveToItem(ItemStack, net.minecraft.core.HolderLookup.Provider) - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.CAPTURED_BLOCK, this); - } - - /** - * @return posSelection stored in given itemStack (or empty instance) - */ - public static CapturedBlock readFromItemStack(final ItemStack itemStack) - { - return itemStack.getOrDefault(ModDataComponents.CAPTURED_BLOCK, CapturedBlock.EMPTY); - } - - /** - * Performs updating of posSelection in given itemStack - */ - public static void updateItemStack(final ItemStack itemStack, final UnaryOperator updater) - { - updater.apply(readFromItemStack(itemStack)).writeToItemStack(itemStack); - } -} diff --git a/src/main/java/com/ldtteam/structurize/component/ModDataComponents.java b/src/main/java/com/ldtteam/structurize/component/ModDataComponents.java deleted file mode 100644 index b3613f44ac..0000000000 --- a/src/main/java/com/ldtteam/structurize/component/ModDataComponents.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.ldtteam.structurize.component; - -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.items.AbstractItemWithPosSelector.PosSelection; -import com.ldtteam.structurize.items.ItemTagTool.TagData; -import com.ldtteam.structurize.util.ScanToolData; -import com.mojang.serialization.Codec; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.neoforged.neoforge.registries.DeferredHolder; -import net.neoforged.neoforge.registries.DeferredRegister; - -public class ModDataComponents -{ - public static final DeferredRegister.DataComponents REGISTRY = DeferredRegister.createDataComponents(Constants.MOD_ID); - - public static final DeferredHolder, DataComponentType> POS_SELECTION = - savedSynced("pos_selection", PosSelection.CODEC, PosSelection.STREAM_CODEC); - public static final DeferredHolder, DataComponentType> TAGS_DATA = - savedSynced("tags", TagData.CODEC, TagData.STREAM_CODEC); - public static final DeferredHolder, DataComponentType> SCAN_TOOL = - savedSynced("scan_tool", ScanToolData.CODEC, ScanToolData.STREAM_CODEC); - public static final DeferredHolder, DataComponentType> CAPTURED_BLOCK = - savedSynced("captured_block", CapturedBlock.CODEC, CapturedBlock.STREAM_CODEC); - - private static DeferredHolder, DataComponentType> savedSynced(final String name, - final Codec codec, - final StreamCodec streamCodec) - { - return REGISTRY.register(name, - () -> DataComponentType.builder().persistent(codec).networkSynchronized(streamCodec).build()); - } -} diff --git a/src/main/java/com/ldtteam/structurize/config/AbstractConfiguration.java b/src/main/java/com/ldtteam/structurize/config/AbstractConfiguration.java new file mode 100644 index 0000000000..edbd8ac651 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/config/AbstractConfiguration.java @@ -0,0 +1,195 @@ +package com.ldtteam.structurize.config; + +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.util.LanguageHandler; +import net.minecraft.server.TickTask; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; +import net.neoforged.neoforge.common.ModConfigSpec.Builder; +import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; +import net.neoforged.neoforge.common.ModConfigSpec.DoubleValue; +import net.neoforged.neoforge.common.ModConfigSpec.EnumValue; +import net.neoforged.neoforge.common.ModConfigSpec.IntValue; +import net.neoforged.neoforge.common.ModConfigSpec.LongValue; +import net.neoforged.neoforge.internal.NeoForgeProxy; +import net.neoforged.neoforge.server.ServerLifecycleHooks; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public abstract class AbstractConfiguration +{ + private static final String DEFAULT_KEY_PREFIX = "structurize.config.default."; + public static final String COMMENT_SUFFIX = ".comment"; + + final List> watchers = new ArrayList<>(); + + protected static void createCategory(final Builder builder, final String key) + { + builder.comment(LanguageHandler.translateKey(commentTKey(key))).push(key); + } + + protected static void swapToCategory(final Builder builder, final String key) + { + finishCategory(builder); + createCategory(builder, key); + } + + protected static void finishCategory(final Builder builder) + { + builder.pop(); + } + + private static String nameTKey(final String key) + { + return Constants.MOD_ID + ".config." + key; + } + + private static String commentTKey(final String key) + { + return nameTKey(key) + COMMENT_SUFFIX; + } + + private static Builder buildBase(final Builder builder, final String key, final String defaultDesc) + { + return builder.comment(LanguageHandler.translateKey(commentTKey(key)) + " " + defaultDesc).translation(nameTKey(key)); + } + + // TODO: inline with icu component + private static String translate(final String key, final Object... args) + { + return LanguageHandler.translateKey(key).formatted(args); + } + + protected static BooleanValue defineBoolean(final Builder builder, final String key, final boolean defaultValue) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "boolean", defaultValue)).define(key, defaultValue); + } + + protected static IntValue defineInteger(final Builder builder, final String key, final int defaultValue) + { + return defineInteger(builder, key, defaultValue, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + protected static IntValue defineInteger(final Builder builder, final String key, final int defaultValue, final int min, final int max) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "number", defaultValue, min, max)).defineInRange(key, defaultValue, min, max); + } + + protected static ConfigValue defineString(final Builder builder, final String key, final String defaultValue) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "string", defaultValue)).define(key, defaultValue); + } + + protected static LongValue defineLong(final Builder builder, final String key, final long defaultValue) + { + return defineLong(builder, key, defaultValue, Long.MIN_VALUE, Long.MAX_VALUE); + } + + protected static LongValue defineLong(final Builder builder, final String key, final long defaultValue, final long min, final long max) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "number", defaultValue, min, max)).defineInRange(key, defaultValue, min, max); + } + + protected static DoubleValue defineDouble(final Builder builder, final String key, final double defaultValue) + { + return defineDouble(builder, key, defaultValue, Double.MIN_VALUE, Double.MAX_VALUE); + } + + protected static DoubleValue defineDouble(final Builder builder, final String key, final double defaultValue, final double min, final double max) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "number", defaultValue, min, max)).defineInRange(key, defaultValue, min, max); + } + + protected static ConfigValue> defineList( + final Builder builder, + final String key, + final List defaultValue, + final Predicate elementValidator) + { + return buildBase(builder, key, "").defineList(key, defaultValue, elementValidator); + } + + protected static > EnumValue defineEnum(final Builder builder, final String key, final V defaultValue) + { + return buildBase(builder, + key, + translate(DEFAULT_KEY_PREFIX + "enum", + defaultValue, + Arrays.stream(defaultValue.getDeclaringClass().getEnumConstants()).map(Enum::name).collect(Collectors.joining(", ")))) + .defineEnum(key, defaultValue); + } + + protected void addWatcher(final ConfigValue configValue, final ConfigListener listener) + { + watchers.add(new ConfigWatcher<>(listener, configValue)); + } + + @SuppressWarnings("unchecked") + protected void addWatcher(final Runnable listener, final ConfigValue... configValues) + { + final ConfigListener typedListener = (o, n) -> listener.run(); + for (final ConfigValue c : configValues) + { + watchers.add(new ConfigWatcher<>(typedListener, (ConfigValue) c)); + } + } + + @FunctionalInterface + public static interface ConfigListener + { + void onChange(T oldValue, T newValue); + } + + /** + * synchronized due to nature of config events + */ + static class ConfigWatcher + { + private final ConfigListener listener; + private final ConfigValue forgeConfig; + + @Nullable + private T lastValue; + + private ConfigWatcher(final ConfigListener listener, final ConfigValue forgeConfig) + { + this.listener = listener; + this.forgeConfig = forgeConfig; + } + + boolean isSameForgeConfig(final ConfigValue other) + { + return other == forgeConfig; + } + + synchronized void cacheLastValue() + { + lastValue = forgeConfig.get(); + } + + synchronized void compareAndFireChangeEvent() + { + final T newValue = forgeConfig.get(); + + if (!Objects.equals(newValue, lastValue)) + { + final Runnable changeEvent = () -> listener.onChange(lastValue, newValue); + if (FMLEnvironment.getDist().isClient()) + { + NeoForgeProxy.INSTANCE.getClientExecutor().schedule(changeEvent); + } + else + { + ServerLifecycleHooks.getCurrentServer().schedule(new TickTask(0, changeEvent)); + } + lastValue = newValue; + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java b/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java index 9341803eb0..c89bc9b1ee 100644 --- a/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java +++ b/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java @@ -1,13 +1,16 @@ package com.ldtteam.structurize.config; -import com.ldtteam.common.config.AbstractConfiguration; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.client.BlueprintHandler; import com.ldtteam.structurize.network.messages.SyncSettingsToServer; import com.ldtteam.structurize.storage.rendering.RenderingCache; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import io.netty.util.internal.shaded.org.jctools.queues.MessagePassingQueue.Consumer; -import net.neoforged.neoforge.common.ModConfigSpec.*; +import net.neoforged.neoforge.common.ModConfigSpec; +import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; +import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; +import net.neoforged.neoforge.common.ModConfigSpec.DoubleValue; +import net.neoforged.neoforge.common.ModConfigSpec.IntValue; /** * Mod client configuration. @@ -29,24 +32,23 @@ public class ClientConfiguration extends AbstractConfiguration * * @param builder config builder */ - public ClientConfiguration(final Builder builder) + protected ClientConfiguration(final ModConfigSpec.Builder builder) { - super(builder, Constants.MOD_ID); - - createCategory("blueprint"); - createCategory("renderer"); + createCategory(builder, "blueprint.renderer"); // if you add anything to this category, also add it #collectPreviewRendererSettings() - renderPlaceholdersNice = defineBoolean("render_placeholders_nice", false); - sharePreviews = defineBoolean("share_previews", false); - displayShared = defineBoolean("see_shared_previews", false); - rendererLightLevel = defineInteger("light_level", 15, -1, 15); - rendererTransparency = defineDouble("transparency", -1, -1, 1); + renderPlaceholdersNice = defineBoolean(builder, "render_placeholders_nice", false); + sharePreviews = defineBoolean(builder, "share_previews", false); + displayShared = defineBoolean(builder, "see_shared_previews", false); + rendererLightLevel = defineInteger(builder, "light_level", 15, -1, 15); + // Keep blueprint presets readable against the world by default. A negative value + // remains available for the legacy solid-preview behavior through the config UI. + rendererTransparency = defineDouble(builder, "transparency", 0.4, -1, 1); addWatcher(BlueprintHandler.getInstance()::clearCache, renderPlaceholdersNice, rendererLightLevel); addWatcher(displayShared, (oldValue, isSharingEnabled) -> { // notify server - new SyncSettingsToServer().sendToServer(); + Network.getNetwork().sendToServer(new SyncSettingsToServer()); if (!isSharingEnabled) { RenderingCache.removeSharedPreviews(); @@ -59,12 +61,12 @@ public ClientConfiguration(final Builder builder) } }); - finishCategory(); // renderer - finishCategory(); // blueprint + finishCategory(builder); // blueprint.renderer + finishCategory(builder); // blueprint - createCategory("gameplay"); - scanToolScrolling = defineBoolean("scan_tool_scrolling", false); - finishCategory(); + createCategory(builder, "gameplay"); + scanToolScrolling = defineBoolean(builder, "scan_tool_scrolling", false); + finishCategory(builder); // gameplay } /** diff --git a/src/main/java/com/ldtteam/structurize/config/Configuration.java b/src/main/java/com/ldtteam/structurize/config/Configuration.java new file mode 100644 index 0000000000..194daa995c --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/config/Configuration.java @@ -0,0 +1,172 @@ +package com.ldtteam.structurize.config; + +import com.ldtteam.structurize.config.AbstractConfiguration.ConfigWatcher; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.config.ConfigTracker; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.common.ModConfigSpec; +import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; +import net.neoforged.neoforge.common.ModConfigSpec.ValueSpec; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.function.Function; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Mod root configuration. + */ +public class Configuration +{ + /** + * Loaded clientside, not synced + */ + private final ModConfig client; + private final ClientConfiguration clientConfig; + /** + * Loaded serverside, synced on connection + */ + private final ModConfig server; + private final ServerConfiguration serverConfig; + + private final ModConfig[] activeModConfigs; + private final AbstractConfiguration[] activeConfigs; + + + /** + * Builds configuration tree. + * + * @param modContainer mod container + * @param modBus mod lifecycle event bus + */ + public Configuration(final ModContainer modContainer, final IEventBus modBus) + { + final Pair ser = + register(ServerConfiguration::new, ModConfig.Type.SERVER, modContainer); + server = ser.getRight(); + serverConfig = ser.getLeft(); + + if (FMLEnvironment.getDist().isClient()) + { + final Pair cli = + register(ClientConfiguration::new, ModConfig.Type.CLIENT, modContainer); + client = cli.getRight(); + clientConfig = cli.getLeft(); + + activeModConfigs = new ModConfig[] {client, server}; + activeConfigs = new AbstractConfiguration[] {clientConfig, serverConfig}; + } + else + { + client = null; + clientConfig = null; + + activeModConfigs = new ModConfig[] {server}; + activeConfigs = new AbstractConfiguration[] {serverConfig}; + } + + modBus.addListener(ModConfigEvent.Loading.class, event -> onConfigLoad(event.getConfig())); + modBus.addListener(ModConfigEvent.Reloading.class, event -> onConfigReload(event.getConfig())); + } + + private Pair register( + final Function factory, + final ModConfig.Type type, + final ModContainer modContainer) + { + if (type == ModConfig.Type.CLIENT && !FMLEnvironment.getDist().isClient()) + { + throw new IllegalStateException("Client configuration cannot be created on the dedicated server"); + } + + final Pair built = new ModConfigSpec.Builder().configure(factory); + return Pair.of(built.getLeft(), ConfigTracker.INSTANCE.registerConfig(type, built.getRight(), modContainer)); + } + + public ClientConfiguration getClient() + { + return clientConfig; + } + + public ServerConfiguration getServer() + { + return serverConfig; + } + + /** + * cache starting values for watchers + */ + private void onConfigLoad(final ModConfig modConfig) + { + if (client != null && modConfig.getSpec() == client.getSpec()) + { + clientConfig.watchers.forEach(ConfigWatcher::cacheLastValue); + } + else if (server != null && modConfig.getSpec() == server.getSpec()) + { + serverConfig.watchers.forEach(ConfigWatcher::cacheLastValue); + } + } + + /** + * iterate watchers and fire changes if needed + */ + private void onConfigReload(final ModConfig modConfig) + { + if (client != null && modConfig.getSpec() == client.getSpec()) + { + clientConfig.watchers.forEach(ConfigWatcher::compareAndFireChangeEvent); + } + else if (server != null && modConfig.getSpec() == server.getSpec()) + { + serverConfig.watchers.forEach(ConfigWatcher::compareAndFireChangeEvent); + } + } + + /** + * Setter wrapper so watchers are fine. + * This should be called from any code that manually changes ConfigValues using set functions. + * (Mostly done by settings UIs) + */ + public void set(final ConfigValue configValue, final T value) + { + configValue.set(value); + configValue.save(); + onConfigValueEdit(configValue); + } + + /** + * This should be called from any code that manually changes ConfigValues using set functions. + * (Mostly done by settings UIs) + * + * @param configValue which config value was changed + */ + public void onConfigValueEdit(final ConfigValue configValue) + { + for (final AbstractConfiguration cfg : activeConfigs) + { + for (final ConfigWatcher configWatcher : cfg.watchers) + { + if (configWatcher.isSameForgeConfig(configValue)) + { + configWatcher.compareAndFireChangeEvent(); + } + } + } + } + + private final Map, Optional> valueSpecCache = new IdentityHashMap<>(); + + /** + * @param value config value from this mod + * @return value spec, crashes in dev if not found + */ + public Optional getSpecFromValue(final ConfigValue value) + { + return Optional.of(value.getSpec()); + } +} diff --git a/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java b/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java index 3bc8b05c79..e28469bf80 100644 --- a/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java +++ b/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java @@ -1,13 +1,10 @@ package com.ldtteam.structurize.config; -import com.ldtteam.common.config.AbstractConfiguration; -import com.ldtteam.structurize.api.constants.Constants; +import com.google.common.collect.Lists; import net.minecraft.core.Direction; -import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; -import net.neoforged.neoforge.common.ModConfigSpec.Builder; -import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; -import net.neoforged.neoforge.common.ModConfigSpec.EnumValue; -import net.neoforged.neoforge.common.ModConfigSpec.IntValue; +import net.neoforged.neoforge.common.ModConfigSpec; + +import java.util.List; /** * Mod server configuration. @@ -20,72 +17,79 @@ public class ServerConfiguration extends AbstractConfiguration /** * Should the default schematics be ignored (from the jar)? */ - public final BooleanValue ignoreSchematicsFromJar; + public final ModConfigSpec.BooleanValue ignoreSchematicsFromJar; /** * Should player made schematics be allowed */ - public final BooleanValue allowPlayerSchematics; + public final ModConfigSpec.BooleanValue allowPlayerSchematics; /** * Max world operations per tick (Max blocks to place, remove or replace) */ - public final IntValue maxOperationsPerTick; + public final ModConfigSpec.IntValue maxOperationsPerTick; /** * Max amount of changes cached to be able to undo */ - public final IntValue maxCachedChanges; + public final ModConfigSpec.IntValue maxCachedChanges; /** * Max amount of schematics to be cached on the server */ - public final IntValue maxCachedSchematics; + public final ModConfigSpec.IntValue maxCachedSchematics; /** * Max amount of blocks checked by a possible worker. */ - public final IntValue maxBlocksChecked; + public final ModConfigSpec.IntValue maxBlocksChecked; /** * Max amount of blocks checked by a possible worker. */ - public final IntValue schematicBlockLimit; + public final ModConfigSpec.IntValue schematicBlockLimit; + + public final ModConfigSpec.ConfigValue iteratorType; - public final ConfigValue iteratorType; + public final ModConfigSpec.ConfigValue> updateStartPos; - public final BooleanValue teleportAllowed; - public final EnumValue teleportBuildDirection; - public final IntValue teleportBuildDistance; - public final BooleanValue teleportSafety; + public final ModConfigSpec.ConfigValue> updateEndPos; + + public final ModConfigSpec.BooleanValue teleportAllowed; + public final ModConfigSpec.EnumValue teleportBuildDirection; + public final ModConfigSpec.IntValue teleportBuildDistance; + public final ModConfigSpec.BooleanValue teleportSafety; /** * Builds server configuration. * * @param builder config builder */ - public ServerConfiguration(final Builder builder) + protected ServerConfiguration(final ModConfigSpec.Builder builder) { - super(builder, Constants.MOD_ID); + createCategory(builder, "gameplay"); + + ignoreSchematicsFromJar = defineBoolean(builder, "ignoreSchematicsFromJar", false); + allowPlayerSchematics = defineBoolean(builder, CONFIG_OPTION_ALLOW_PLAYER_SCHEMATICS, false); + maxOperationsPerTick = defineInteger(builder, "maxOperationsPerTick", 1000, 0, 100000); + maxCachedChanges = defineInteger(builder, "maxCachedChanges", 50, 0, 250); + maxCachedSchematics = defineInteger(builder, "maxCachedSchematics", 100, 0, 100000); + maxBlocksChecked = defineInteger(builder, "maxBlocksChecked", 1000, 0, 100000); + schematicBlockLimit = defineInteger(builder, "schematicBlockLimit", 100000, 1000, 1000000); + iteratorType = defineString(builder, "iteratorType", "default"); - createCategory("gameplay"); + swapToCategory(builder, "teleport"); - ignoreSchematicsFromJar = defineBoolean("ignoreSchematicsFromJar", false); - allowPlayerSchematics = defineBoolean(CONFIG_OPTION_ALLOW_PLAYER_SCHEMATICS, false); - maxOperationsPerTick = defineInteger("maxOperationsPerTick", 1000, 0, 100000); - maxCachedChanges = defineInteger("maxCachedChanges", 50, 0, 250); - maxCachedSchematics = defineInteger("maxCachedSchematics", 100, 0, 100000); - maxBlocksChecked = defineInteger("maxBlocksChecked", 1000, 0, 100000); - schematicBlockLimit = defineInteger("schematicBlockLimit", 100000, 1000, 1000000); - iteratorType = defineString("iteratorType", "default"); + teleportAllowed = defineBoolean(builder, "teleportAllowed", true); + teleportBuildDirection = defineEnum(builder, "teleportBuildDirection", Direction.SOUTH); + teleportBuildDistance = defineInteger(builder, "teleportBuildDistance", 3, 1, 16); + teleportSafety = defineBoolean(builder, "teleportSafety", true); - swapToCategory("teleport"); + swapToCategory(builder, "update"); - teleportAllowed = defineBoolean("teleportAllowed", true); - teleportBuildDirection = defineEnum("teleportBuildDirection", Direction.SOUTH); - teleportBuildDistance = defineInteger("teleportBuildDistance", 3, 1, 16); - teleportSafety = defineBoolean("teleportSafety", true); + updateStartPos = builder.define("start", Lists.newArrayList(-10,-10)); + updateEndPos = builder.define("end", Lists.newArrayList(10,10)); - finishCategory(); + finishCategory(builder); } } diff --git a/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java b/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java index 47021683cf..fe9728e2e0 100644 --- a/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java +++ b/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java @@ -1,43 +1,43 @@ -package com.ldtteam.structurize.datagen; - -import com.ldtteam.domumornamentum.entity.block.ModBlockEntityTypes; -import com.ldtteam.domumornamentum.util.Constants; -import com.ldtteam.structurize.tag.ModTags; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.data.PackOutput; -import net.minecraft.data.tags.IntrinsicHolderTagsProvider; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.neoforged.neoforge.common.data.ExistingFileHelper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; - -/** - * Datagen provider for Block Entity Tags - */ -public class BlockEntityTagProvider extends IntrinsicHolderTagsProvider> -{ - - public BlockEntityTagProvider( - final PackOutput output, - final ResourceKey>> key, - final CompletableFuture provider, - @Nullable final ExistingFileHelper existingFileHelper) - { - super(output, key, provider, k -> BuiltInRegistries.BLOCK_ENTITY_TYPE.getResourceKey(k).get(), Constants.MOD_ID, existingFileHelper); - } - - @Override - protected void addTags(HolderLookup.@NotNull Provider provider) - { - this.tag(ModTags.SUBSTITUTION_ABSORB_WHITELIST) - .add(BlockEntityType.CHEST) - .add(BlockEntityType.SIGN) - .add(BlockEntityType.LECTERN) - .add(ModBlockEntityTypes.MATERIALLY_TEXTURED.get()); - } -} +package com.ldtteam.structurize.datagen; + +import com.ldtteam.domumornamentum.entity.block.ModBlockEntityTypes; +import com.ldtteam.domumornamentum.util.Constants; +import com.ldtteam.structurize.tag.ModTags; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.core.Registry; +import net.minecraft.data.PackOutput; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.BlockEntityTypes; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +/** + * Datagen provider for Block Entity Tags + */ +public class BlockEntityTagProvider extends TagsProvider> +{ + + public BlockEntityTagProvider( + final PackOutput output, + final ResourceKey>> key, + final CompletableFuture provider) + { + super(output, key, provider, Constants.MOD_ID); + } + + @Override + protected void addTags(HolderLookup.@NotNull Provider provider) + { + this.tag(ModTags.SUBSTITUTION_ABSORB_WHITELIST) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("minecraft:chest"))) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("minecraft:sign"))) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("minecraft:lectern"))) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("domum_ornamentum:materially_textured"))); + } +} diff --git a/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java b/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java index a2dc61fc28..ba399d5f17 100644 --- a/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java +++ b/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java @@ -1,53 +1,54 @@ -package com.ldtteam.structurize.datagen; - -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.tag.ModTags; -import com.ldtteam.structurize.util.BlockUtils; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.data.PackOutput; -import net.minecraft.data.tags.IntrinsicHolderTagsProvider; -import net.minecraft.resources.ResourceKey; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.Fallable; -import net.minecraft.world.level.block.FallingBlock; -import net.neoforged.neoforge.common.data.ExistingFileHelper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.concurrent.CompletableFuture; - -/** - * Datagen provider for Block Tags - */ -public class BlockTagProvider extends IntrinsicHolderTagsProvider -{ - public BlockTagProvider(final PackOutput output, - final ResourceKey> key, - final CompletableFuture provider, - @Nullable final ExistingFileHelper existingFileHelper) - { - super(output, key, provider, k -> BuiltInRegistries.BLOCK.getResourceKey(k).get(), Constants.MOD_ID, existingFileHelper); - } - - @Override - protected void addTags(HolderLookup.@NotNull Provider provider) - { - final IntrinsicTagAppender weakSolidTag = this.tag(ModTags.WEAK_SOLID_BLOCKS).addTag(BlockTags.LEAVES); - - provider.lookupOrThrow(Registries.BLOCK) - .filterElements(block -> block instanceof Fallable || block instanceof FallingBlock) - .filterElements(BlockUtils::canBlockSurviveWithoutSupport) - .listElementIds() - .forEach(weakSolidTag::add); - - this.tag(ModTags.UNSUITABLE_SOLID_FOR_PLACEHOLDER).addTag(BlockTags.LEAVES); - - this.tag(ModTags.GOOD_SOLID_FOR_PLACEHOLDER).add(Blocks.FARMLAND); - - this.tag(ModTags.BLUEPRINT_BLACKLIST); - } -} +package com.ldtteam.structurize.datagen; + +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.tag.ModTags; +import com.ldtteam.structurize.util.BlockUtils; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.PackOutput; +import net.minecraft.data.tags.TagAppender; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Fallable; +import net.minecraft.world.level.block.FallingBlock; +import org.jetbrains.annotations.NotNull; +import java.util.concurrent.CompletableFuture; + +/** + * Datagen provider for Block Tags + */ +public class BlockTagProvider extends TagsProvider +{ + public BlockTagProvider(final PackOutput output, + final ResourceKey> key, + final CompletableFuture provider) + { + super(output, key, provider, Constants.MOD_ID); + } + + @Override + protected void addTags(HolderLookup.@NotNull Provider provider) + { + final TagAppender weakSolidTag = this.tag(ModTags.WEAK_SOLID_BLOCKS); + weakSolidTag.addTag(BlockTags.LEAVES); + + provider.lookupOrThrow(Registries.BLOCK) + .listElements() + .map(entry -> entry.value()) + .filter(block -> block instanceof Fallable || block instanceof FallingBlock) + .filter(BlockUtils::canBlockSurviveWithoutSupport) + .forEach(block -> weakSolidTag.add(ResourceKey.create(Registries.BLOCK, BuiltInRegistries.BLOCK.getKey(block)))); + + this.tag(ModTags.UNSUITABLE_SOLID_FOR_PLACEHOLDER).addTag(BlockTags.LEAVES); + + this.tag(ModTags.GOOD_SOLID_FOR_PLACEHOLDER).add(ResourceKey.create(Registries.BLOCK, Identifier.parse("minecraft:farmland"))); + + this.tag(ModTags.BLUEPRINT_BLACKLIST); + } +} diff --git a/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java b/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java index 51de8f6806..6c40dd2847 100644 --- a/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java +++ b/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java @@ -1,40 +1,41 @@ package com.ldtteam.structurize.datagen; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.tag.ModTags; import net.minecraft.core.HolderLookup.Provider; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; import net.minecraft.core.Registry; import net.minecraft.data.PackOutput; -import net.minecraft.data.tags.IntrinsicHolderTagsProvider; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.EntityType; -import net.neoforged.neoforge.common.data.ExistingFileHelper; -import org.jetbrains.annotations.Nullable; +import net.minecraft.world.entity.EntityTypes; import java.util.concurrent.CompletableFuture; /** * Datagen provider for Entity Tags */ -public class EntityTagProvider extends IntrinsicHolderTagsProvider> +public class EntityTagProvider extends TagsProvider> { public EntityTagProvider(final PackOutput output, final ResourceKey>> key, - final CompletableFuture future, - @Nullable final ExistingFileHelper existingFileHelper) + final CompletableFuture future) { - super(output, key, future, k -> BuiltInRegistries.ENTITY_TYPE.getResourceKey(k).get(), Constants.MOD_ID, existingFileHelper); + super(output, key, future, Constants.MOD_ID); } @Override protected void addTags(final Provider provider) { - tag(ModTags.PREVIEW_TICKING_ENTITIES).add(EntityType.ARMOR_STAND) - .add(EntityType.END_CRYSTAL) - .add(EntityType.BLOCK_DISPLAY) - .add(EntityType.ITEM_DISPLAY) - .add(EntityType.TEXT_DISPLAY) - .add(EntityType.FURNACE_MINECART) - .add(EntityType.OMINOUS_ITEM_SPAWNER); + // 1.20.2 tick: armorstand, endcrystal, minecartfurnace, display + + tag(ModTags.PREVIEW_TICKING_ENTITIES).add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:armor_stand"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:end_crystal"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:block_display"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:item_display"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:text_display"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:furnace_minecart"))); } } diff --git a/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java b/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java index ae88f985a5..4b38c21153 100644 --- a/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java @@ -1,43 +1,68 @@ package com.ldtteam.structurize.event; import com.ldtteam.blockui.BOScreen; -import com.ldtteam.structurize.api.IScrollableItem; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.IScrollableItem; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; +import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.client.BlueprintHandler; +import com.ldtteam.structurize.client.BlueprintRenderer; import com.ldtteam.structurize.client.ModKeyMappings; import com.ldtteam.structurize.client.gui.WindowExtendedBuildTool; +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; import com.ldtteam.structurize.items.ItemScanTool; +import com.ldtteam.structurize.items.ItemTagTool; +import com.ldtteam.structurize.items.ModItems; import com.ldtteam.structurize.network.messages.ItemMiddleMouseMessage; import com.ldtteam.structurize.network.messages.ScanToolTeleportMessage; import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; +import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import com.ldtteam.structurize.client.rendertask.util.BufferSourceCompat; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.KeyMapping; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.resources.Identifier; + import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; -import net.neoforged.bus.api.EventPriority; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent.LoggingOut; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.AABB; +import net.minecraft.gizmos.GizmoStyle; +import net.minecraft.gizmos.Gizmos; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.neoforge.client.event.InputEvent; import net.neoforged.neoforge.client.event.RenderGuiLayerEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; +import net.neoforged.neoforge.client.event.SubmitCustomGeometryEvent; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; +import net.neoforged.bus.api.SubscribeEvent; import org.jetbrains.annotations.NotNull; -import java.util.Iterator; +import java.util.List; import java.util.Map; public class ClientEventSubscriber { @SubscribeEvent - public static void renderWorldLastEvent(final RenderGuiLayerEvent.Pre event) + public static void hideVanillaStatusOverlays(final RenderGuiLayerEvent.Pre event) { - if ((event.getName().equals(VanillaGuiLayers.PLAYER_HEALTH) || event.getName().equals(VanillaGuiLayers.FOOD_LEVEL)) && Minecraft.getInstance().screen instanceof BOScreen && - ((BOScreen) Minecraft.getInstance().screen).getWindow() instanceof WindowExtendedBuildTool) + final Identifier layer = event.getName(); + if ((layer.equals(VanillaGuiLayers.PLAYER_HEALTH) || layer.equals(VanillaGuiLayers.FOOD_LEVEL)) && Minecraft.getInstance().gui.screen() instanceof BOScreen && + ((BOScreen) Minecraft.getInstance().gui.screen()).getWindow() instanceof WindowExtendedBuildTool) { event.setCanceled(true); } @@ -45,20 +70,86 @@ public static void renderWorldLastEvent(final RenderGuiLayerEvent.Pre event) /** - * Used to catch the renderWorldLastEvent in order to draw the debug nodes for pathfinding. - * - * @param event the catched event. + * Submit build-tool previews while the level renderer is collecting the + * current frame's feature nodes. RenderLevelStageEvent is fired after the + * feature frame has already been prepared, so submissions made there are + * discarded by Minecraft 26.2. */ @SubscribeEvent - public static void renderWorldLastEvent(final RenderLevelStageEvent event) + public static void submitBlueprints(final SubmitCustomGeometryEvent event) { - WorldRenderContext.INSTANCE.renderWorldLastEvent(event); + final Minecraft mc = Minecraft.getInstance(); + if (mc.level == null || mc.player == null) + { + return; + } + + try (final Gizmos.TemporaryCollection ignored = mc.levelRenderer.collectPerFrameRenderThreadGizmos()) + { + for (final BlueprintPreviewData previewData : RenderingCache.getBlueprintsToRender()) + { + final Blueprint blueprint = previewData.getBlueprint(); + final BlockPos pos = previewData.getPos(); + if (blueprint == null || pos == null) + { + continue; + } + + BlueprintHandler.getInstance().internalBackportDraw(previewData, pos, event); + + final BlockPos anchor = pos.subtract(blueprint.getPrimaryBlockOffset()); + final BlockPos max = anchor.offset(blueprint.getSizeX(), blueprint.getSizeY(), blueprint.getSizeZ()); + Gizmos.cuboid(new AABB(Vec3.atLowerCornerOf(anchor), Vec3.atLowerCornerOf(max)), GizmoStyle.stroke(0xffffffff, 2.5f)); + Gizmos.cuboid(new AABB(pos), GizmoStyle.stroke(0xffff0000, 2.5f)).setAlwaysOnTop(); + } + } } - @SubscribeEvent(priority = EventPriority.LOWEST) - public static void finishBuffers(final RenderLevelStageEvent event) + @SubscribeEvent + public static void renderAfterBlockFeatures(final RenderLevelStageEvent.AfterOpaqueFeatures event) { - WorldRenderContext.RenderTypes.finishBuffer(event); + WorldEventRenderContext.INSTANCE.renderWorldLastEvent(event); + renderTagTool(Minecraft.getInstance(), event.getPoseStack(), WorldRenderMacros.getBufferSource(), + Minecraft.getInstance().gameRenderer.mainCamera().position()); + WorldRenderMacros.getBufferSource().endBatch(); + } + + private static void renderTagTool(final Minecraft mc, + final PoseStack matrixStack, + final BufferSourceCompat bufferSource, + final Vec3 viewPosition) + { + final Player player = mc.player; + final ItemStack itemStack = player.getItemInHand(InteractionHand.MAIN_HAND); + if (itemStack.getItem() == ModItems.tagTool.get() && ItemStackNbtHelper.hasCustomTag(itemStack) + && ItemStackNbtHelper.getCustomTag(itemStack).contains(ItemTagTool.TAG_ANCHOR_POS)) + { + Profiler.get().push("struct_tags"); + + final BlockPos tagAnchor = BlockPosUtil.readFromNBT(ItemStackNbtHelper.getCustomTag(itemStack), ItemTagTool.TAG_ANCHOR_POS); + final Vec3 realRenderRootVecd = Vec3.atLowerCornerOf(tagAnchor).subtract(viewPosition); + final BlockEntity te = player.level().getBlockEntity(tagAnchor); + + matrixStack.pushPose(); + matrixStack.translate(realRenderRootVecd.x(), realRenderRootVecd.y(), realRenderRootVecd.z()); + + if (te instanceof final IBlueprintDataProviderBE blueprintProvider) + { + final Map> tagPosList = blueprintProvider.getWorldTagPosMap(); + + for (final Map.Entry> entry : tagPosList.entrySet()) + { + final BlockPos pos = entry.getKey().subtract(tagAnchor); + WorldRenderMacros.renderWhiteLineBox(bufferSource, matrixStack, pos, pos, 0.025f); + WorldRenderMacros.renderDebugText(pos, entry.getKey(), entry.getValue(), matrixStack, true, 3, bufferSource); + } + } + WorldRenderMacros.renderRedGlintLineBox(bufferSource, matrixStack, BlockPos.ZERO, BlockPos.ZERO, 0.025f); + + matrixStack.popPose(); + + Profiler.get().pop(); + } } /** @@ -71,13 +162,13 @@ public static void finishBuffers(final RenderLevelStageEvent event) public static void onClientTickEvent(final ClientTickEvent.Post event) { final Minecraft mc = Minecraft.getInstance(); - mc.getProfiler().push("structurize"); + Profiler.get().push("structurize"); if (mc.level != null && mc.level.getGameTime() % (Constants.TICKS_SECOND * BlueprintHandler.CACHE_EXPIRE_CHECK_SECONDS) == 0) { - mc.getProfiler().push("blueprint_manager_tick"); + Profiler.get().push("blueprint_manager_tick"); BlueprintHandler.getInstance().cleanCache(); - mc.getProfiler().pop(); + Profiler.get().pop(); } if (ModKeyMappings.TELEPORT.get().consumeClick() && mc.level != null && mc.player != null && @@ -85,18 +176,20 @@ public static void onClientTickEvent(final ClientTickEvent.Post event) { if (tool.onTeleport(mc.player, mc.player.getMainHandItem())) { - new ScanToolTeleportMessage().sendToServer(); + Network.getNetwork().sendToServer(new ScanToolTeleportMessage()); } } - mc.getProfiler().pop(); + Profiler.get().pop(); } @SubscribeEvent public static void onPreClientTickEvent(@NotNull final ClientTickEvent.Pre event) { + RenderTaskManager.onClientTick(); + final Minecraft mc = Minecraft.getInstance(); - if (mc.player == null || mc.screen != null || mc.level == null) return; + if (mc.player == null || mc.gui.screen() != null || mc.level == null) return; if (mc.options.keyPickItem.consumeClick()) { @@ -106,34 +199,24 @@ public static void onPreClientTickEvent(@NotNull final ClientTickEvent.Pre event pos = null; } - final ItemStack current = mc.player.getInventory().getSelected(); + final ItemStack current = mc.player.getInventory().getSelectedItem(); if (current.getItem() instanceof ISpecialBlockPickItem clickableItem) { - final boolean ctrlKey = Screen.hasControlDown(); - switch (clickableItem.onBlockPick(mc.player, current, pos, ctrlKey)) - { - case PASS: - ++mc.options.keyPickItem.clickCount; - break; - case FAIL: - break; - default: - new ItemMiddleMouseMessage(pos, ctrlKey).sendToServer(); - break; - } + final boolean ctrlKey = mc.hasControlDown(); + final InteractionResult pickResult = clickableItem.onBlockPick(mc.player, current, pos, ctrlKey); + if (pickResult == InteractionResult.PASS) + { + KeyMapping.click(mc.options.keyPickItem.getKey()); + } + else if (pickResult != InteractionResult.FAIL) + { + KeyMapping.click(mc.options.keyPickItem.getKey()); + Network.getNetwork().sendToServer(new ItemMiddleMouseMessage(pos, ctrlKey)); + } } else { - ++mc.options.keyPickItem.clickCount; - } - } - - for (Iterator> iterator = RenderingCache.boxRenderingCache.entrySet().iterator(); iterator.hasNext(); ) - { - final var entry = iterator.next(); - if (entry.getValue().isExpired()) - { - iterator.remove(); + KeyMapping.click(mc.options.keyPickItem.getKey()); } } } @@ -142,32 +225,24 @@ public static void onPreClientTickEvent(@NotNull final ClientTickEvent.Pre event public static void onMouseWheel(final InputEvent.MouseScrollingEvent event) { final Minecraft mc = Minecraft.getInstance(); - if (event.isCanceled() || mc.player == null || mc.screen != null || mc.level == null) return; + if (event.isCanceled() || mc.player == null || mc.gui.screen() != null || mc.level == null) return; if (!mc.player.isShiftKeyDown()) return; - final ItemStack current = mc.player.getInventory().getSelected(); + final ItemStack current = mc.player.getInventory().getSelectedItem(); if (current.getItem() instanceof IScrollableItem scrollableItem) { - final boolean ctrlKey = Screen.hasControlDown(); - switch (scrollableItem.onMouseScroll(mc.player, current, event.getScrollDeltaX(), event.getScrollDeltaY(), ctrlKey)) + final boolean ctrlKey = mc.hasControlDown(); + final InteractionResult scrollResult = + scrollableItem.onMouseScroll(mc.player, current, event.getScrollDeltaY(), ctrlKey); + if (scrollResult == InteractionResult.FAIL) { - case PASS: - break; - case FAIL: - event.setCanceled(true); - break; - default: - event.setCanceled(true); - new ItemMiddleMouseMessage(event.getScrollDeltaX(), event.getScrollDeltaY(), ctrlKey).sendToServer(); - break; + event.setCanceled(true); + } + else if (scrollResult != InteractionResult.PASS) + { + event.setCanceled(true); + Network.getNetwork().sendToServer(new ItemMiddleMouseMessage(event.getScrollDeltaY(), ctrlKey)); } } } - - @SubscribeEvent - public static void onDisconnect(final LoggingOut event) - { - // clear local caches - WindowExtendedBuildTool.clearStaticData(); - } } diff --git a/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java b/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java index 2e9ab6b64d..a2dfff0391 100644 --- a/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java @@ -1,79 +1,68 @@ package com.ldtteam.structurize.event; import com.ldtteam.structurize.blockentities.ModBlockEntities; -import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.client.*; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.client.model.OverlaidModelLoader; import com.ldtteam.structurize.items.ItemStackTooltip; -import com.ldtteam.structurize.items.ModItems; -import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.ContainerPlacementHandler; import com.ldtteam.structurize.storage.ClientStructurePackLoader; -import com.ldtteam.structurize.util.WorldRenderMacros; -import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; -import net.minecraft.client.renderer.ItemBlockRenderTypes; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.SimplePreparableReloadListener; import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.world.level.block.Block; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; -import net.neoforged.bus.api.EventPriority; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; -import net.neoforged.neoforge.capabilities.Capabilities.ItemHandler; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; import net.neoforged.neoforge.client.event.RegisterClientTooltipComponentFactoriesEvent; import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; -import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent; -import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; -import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; public class ClientLifecycleSubscriber { + /** + * Called when client app is initialized. + * + * @param event event + */ @SubscribeEvent public static void onClientInit(final FMLClientSetupEvent event) { + // Minecraft is initialized by FMLClientSetupEvent; starting discovery from + // the mod constructor can observe a null client and permanently skip all + // local structure packs. ClientStructurePackLoader.onClientLoading(); - } - @SubscribeEvent - public static void onRegisterReloadListeners(final RegisterClientReloadListenersEvent event) - { - event.registerReloadListener(new SimplePreparableReloadListener<>() + final ResourceManager rm = Minecraft.getInstance().getResourceManager(); + if (rm instanceof final ReloadableResourceManager resourceManager) { - @Override - protected Object prepare(final ResourceManager manager, final ProfilerFiller profiler) + resourceManager.registerReloadListener(new SimplePreparableReloadListener<>() { - return new Object(); - } - @Override - protected void apply(final Object source, final ResourceManager manager, final ProfilerFiller profiler) - { - Log.getLogger().debug("Clearing blueprint renderer cache."); - BlueprintHandler.getInstance().clearCache(); - } - }); - } + @Override + protected Object prepare(final ResourceManager manager, final ProfilerFiller profiler) + { + return new Object(); + } - @OnlyIn(Dist.CLIENT) - @SubscribeEvent - public static void doClientStuff(final EntityRenderersEvent.RegisterRenderers event) - { - ItemBlockRenderTypes.setRenderLayer(ModBlocks.blockSubstitution.get(), RenderType.translucent()); + @Override + protected void apply(final Object source, final ResourceManager manager, final ProfilerFiller profiler) + { + Log.getLogger().debug("Clearing blueprint renderer cache."); + BlueprintHandler.getInstance().clearCache(); + } + }); + } } @SubscribeEvent - public static void registerGeometry(final ModelEvent.RegisterGeometryLoaders event) + public static void registerModelLoaders(final ModelEvent.RegisterLoaders event) { - event.register(Constants.resLocStruct("overlaid"), new OverlaidModelLoader()); + event.register(Identifier.fromNamespaceAndPath("structurize", "overlaid"), new OverlaidModelLoader()); } @SubscribeEvent @@ -94,22 +83,4 @@ public static void registerKeys(final RegisterKeyMappingsEvent event) ModKeyMappings.register(event); } - @SubscribeEvent - public static void registerGlobablRenderBuffers(final RegisterRenderBuffersEvent event) - { - WorldRenderMacros.RenderTypes.registerBuffer(event); - } - - @SubscribeEvent - public static void registerClientExtensions(final RegisterClientExtensionsEvent event) - { - event.registerItem(new IClientItemExtensions() - { - @Override - public BlockEntityWithoutLevelRenderer getCustomRenderer() - { - return TagSubstitutionRenderer.getInstance(); - } - }, ModItems.blockTagSubstitution.get()); - } } diff --git a/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java b/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java index ef97526ece..0ca9320179 100644 --- a/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java @@ -1,14 +1,20 @@ package com.ldtteam.structurize.event; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.commands.EntryPoint; import com.ldtteam.structurize.management.Manager; +import com.ldtteam.structurize.network.messages.ServerUUIDMessage; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.IOPool; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerLevel; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.neoforge.event.tick.LevelTickEvent; +import net.neoforged.bus.api.SubscribeEvent; + import org.jetbrains.annotations.NotNull; /** @@ -37,14 +43,33 @@ public static void onRegisterCommands(final RegisterCommandsEvent event) EntryPoint.register(event.getDispatcher(), event.getCommandSelection()); } + /** + * Called when a player logs in. If the joining player is a MP-Player, sends + * all possible styles in a message. + * + * @param event the login event + */ + @SubscribeEvent + public static void onPlayerLogin(final PlayerEvent.PlayerLoggedInEvent event) + { + if (event.getEntity() instanceof ServerPlayer serverPlayer) + { + Network.getNetwork().sendToPlayer(new ServerUUIDMessage(), serverPlayer); + } + } + @SubscribeEvent public static void onWorldTick(final LevelTickEvent.Pre event) { - BlockUtils.checkOrInit(); if (event.getLevel() instanceof ServerLevel serverLevel) { + BlockUtils.checkOrInit(); Manager.onWorldTick(serverLevel); } + else if (event.getLevel() instanceof ClientLevel) + { + BlockUtils.checkOrInit(); + } } @SubscribeEvent diff --git a/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java b/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java index 5b07934211..45328d4a9e 100644 --- a/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java @@ -1,17 +1,15 @@ package com.ldtteam.structurize.event; -import com.ldtteam.common.language.LanguageHandler; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.datagen.BlockEntityTagProvider; import com.ldtteam.structurize.datagen.BlockTagProvider; import com.ldtteam.structurize.datagen.EntityTagProvider; -import com.ldtteam.structurize.network.messages.*; -import com.ldtteam.structurize.storage.ServerStructurePackLoader; +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.util.LanguageHandler; import net.minecraft.core.registries.Registries; import net.minecraft.data.DataGenerator; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModList; -import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent; import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent; import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; @@ -21,36 +19,11 @@ public class LifecycleSubscriber { @SubscribeEvent - public static void onNetworkRegistry(final RegisterPayloadHandlersEvent event) + public static void onRegisterPayloads(final RegisterPayloadHandlersEvent event) { final String modVersion = ModList.get().getModContainerById(Constants.MOD_ID).get().getModInfo().getVersion().toString(); - final PayloadRegistrar registry = event.registrar(Constants.MOD_ID).versioned(modVersion); - - AbsorbBlockMessage.TYPE.register(registry); - AddRemoveTagMessage.TYPE.register(registry); - BlueprintSyncMessage.TYPE.register(registry); - BuildToolPlacementMessage.TYPE.register(registry); - ClientBlueprintRequestMessage.TYPE.register(registry); - FillTopPlaceholderMessage.TYPE.register(registry); - ItemMiddleMouseMessage.TYPE.register(registry); - NotifyClientAboutStructurePacksMessage.TYPE.register(registry); - NotifyServerAboutStructurePacksMessage.TYPE.register(registry); - OperationHistoryMessage.TYPE.register(registry); - RemoveBlockMessage.TYPE.register(registry); - RemoveEntityMessage.TYPE.register(registry); - ReplaceBlockMessage.TYPE.register(registry); - SaveScanMessage.TYPE.register(registry); - ScanOnServerMessage.TYPE.register(registry); - ScanToolTeleportMessage.TYPE.register(registry); - SetTagInTool.TYPE.register(registry); - ShowScanMessage.TYPE.register(registry); - SyncPreviewCacheToClient.TYPE.register(registry); - SyncPreviewCacheToServer.TYPE.register(registry); - SyncSettingsToServer.TYPE.register(registry); - TransferStructurePackToClient.TYPE.register(registry); - UndoRedoMessage.TYPE.register(registry); - UpdateClientRender.TYPE.register(registry); - UpdateScanToolMessage.TYPE.register(registry); + final PayloadRegistrar registrar = event.registrar(Constants.MOD_ID).versioned(modVersion); + Network.getNetwork().registerCommonMessages(registrar); } /** @@ -65,17 +38,20 @@ public static void onLoadComplete(final FMLLoadCompleteEvent event) } @SubscribeEvent - public static void onDedicatedServerInit(final FMLDedicatedServerSetupEvent event) + public static void onServerDatagen(@NotNull final GatherDataEvent.Server event) { - ServerStructurePackLoader.onServerStarting(); + final DataGenerator generator = event.getGenerator(); + event.addProvider(new BlockEntityTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK_ENTITY_TYPE, event.getLookupProvider())); + event.addProvider(new BlockTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK, event.getLookupProvider())); } @SubscribeEvent - public static void onDatagen(@NotNull final GatherDataEvent event) + public static void onClientDatagen(@NotNull final GatherDataEvent.Client event) { final DataGenerator generator = event.getGenerator(); - generator.addProvider(event.includeServer(), new BlockEntityTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK_ENTITY_TYPE, event.getLookupProvider(), event.getExistingFileHelper())); - generator.addProvider(event.includeServer(), new BlockTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK, event.getLookupProvider(), event.getExistingFileHelper())); - generator.addProvider(event.includeClient(), new EntityTagProvider(event.getGenerator().getPackOutput(), Registries.ENTITY_TYPE, event.getLookupProvider(), event.getExistingFileHelper())); + if (event instanceof GatherDataEvent.Client) + { + event.addProvider(new EntityTagProvider(event.getGenerator().getPackOutput(), Registries.ENTITY_TYPE, event.getLookupProvider())); + } } } diff --git a/src/main/java/com/ldtteam/structurize/event/WorldRenderContext.java b/src/main/java/com/ldtteam/structurize/event/WorldRenderContext.java deleted file mode 100644 index d5073f731d..0000000000 --- a/src/main/java/com/ldtteam/structurize/event/WorldRenderContext.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.ldtteam.structurize.event; - -import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; -import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.client.BlueprintRenderer.TransparencyHack; -import com.ldtteam.structurize.items.ItemTagTool.TagData; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; -import com.ldtteam.structurize.util.WorldRenderMacros; -import net.minecraft.core.BlockPos; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage; - -import java.util.List; -import java.util.Map; - -/** - * For rendering into world. - */ -public class WorldRenderContext extends WorldRenderMacros -{ - static final WorldRenderContext INSTANCE = new WorldRenderContext(); - - @Override - protected void renderWithinContext(final Stage stage) - { - final double alpha = Structurize.getConfig().getClient().rendererTransparency.get(); - final boolean isAlphaApplied = alpha > 0 && alpha < TransparencyHack.THRESHOLD; - - final Stage when = isAlphaApplied ? Stage.AFTER_TRANSLUCENT_BLOCKS : Stage.AFTER_BLOCK_ENTITIES; - // otherwise even worse sorting issues arise - if (stage == when) - { - renderBlueprints(); - } - - if (stage == WorldRenderMacros.STAGE_FOR_LINES) - { - renderBoxes(); - renderTagTool(); - } - } - - private void renderBlueprints() - { - for (final BlueprintPreviewData previewData : RenderingCache.getBlueprintsToRender()) - { - final Blueprint blueprint = previewData.getBlueprint(); - - if (blueprint != null) - { - mc.getProfiler().push("struct_render"); - - renderBlueprint(previewData, previewData.getPos()); - - mc.getProfiler().pop(); - } - } - } - - private void renderBoxes() - { - for (final BlueprintPreviewData previewData : RenderingCache.getBlueprintsToRender()) - { - final Blueprint blueprint = previewData.getBlueprint(); - - if (blueprint != null) - { - final BlockPos anchor = blueprint.getPrimaryBlockOffset(); - - mc.getProfiler().push("struct_render"); - pushPoseCameraToPos(previewData.getPos().subtract(anchor)); - - renderWhiteLineBox(BlockPos.ZERO, - new BlockPos(blueprint.getSizeX() - 1, blueprint.getSizeY() - 1, blueprint.getSizeZ() - 1), - DEFAULT_LINE_WIDTH); - renderRedGlintLineBox(anchor, anchor, DEFAULT_LINE_WIDTH); - - popPose(); - mc.getProfiler().pop(); - } - } - - for (final BoxPreviewData previewData : RenderingCache.getBoxesToRender()) - { - final BlockPos root = previewData.pos1(); - - mc.getProfiler().push("struct_box"); - pushPoseCameraToPos(root); - - // Used to render a red box around a scan's Primary offset (primary block) - renderWhiteLineBox(BlockPos.ZERO, previewData.pos2().subtract(root), DEFAULT_LINE_WIDTH); - previewData.anchor().map(pos -> pos.subtract(root)).ifPresent(pos -> renderRedGlintLineBox(pos, pos, DEFAULT_LINE_WIDTH)); - - popPose(); - mc.getProfiler().pop(); - } - } - - private void renderTagTool() - { - final Player player = mc.player; - final ItemStack itemStack = player.getItemInHand(InteractionHand.MAIN_HAND); - final TagData tags = TagData.readFromItemStack(itemStack); - if (tags.anchorPos().isPresent()) - { - final BlockPos tagAnchor = tags.anchorPos().get(); - final BlockEntity te = player.level().getBlockEntity(tagAnchor); - - mc.getProfiler().push("struct_tags"); - pushPoseCameraToPos(tagAnchor); - - if (te instanceof final IBlueprintDataProviderBE blueprintProvider) - { - final Map> tagPosList = blueprintProvider.getWorldTagPosMap(); - - for (final Map.Entry> entry : tagPosList.entrySet()) - { - final BlockPos pos = entry.getKey().subtract(tagAnchor); - renderWhiteLineBox(pos, pos, DEFAULT_LINE_WIDTH); - renderDebugText(pos, entry.getKey(), entry.getValue(), true, 3); - } - } - renderRedGlintLineBox(BlockPos.ZERO, BlockPos.ZERO, DEFAULT_LINE_WIDTH); - - popPose(); - mc.getProfiler().pop(); - } - } -} diff --git a/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java b/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java index 341a68f913..3fcc37865a 100644 --- a/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java +++ b/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java @@ -1,28 +1,26 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.component.ModDataComponents; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.network.RegistryFriendlyByteBuf; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import net.minecraft.network.chat.Component; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResult.Success; import net.minecraft.world.InteractionHand; import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponentType; import net.minecraft.world.level.Level; -import java.util.Optional; -import java.util.function.UnaryOperator; +import static com.ldtteam.structurize.api.util.constant.NbtTagConstants.FIRST_POS_STRING; +import static com.ldtteam.structurize.api.util.constant.NbtTagConstants.SECOND_POS_STRING; + import org.jetbrains.annotations.NotNull; /** @@ -30,6 +28,8 @@ */ public abstract class AbstractItemWithPosSelector extends Item { + private static final String NBT_START_POS = FIRST_POS_STRING; + private static final String NBT_END_POS = SECOND_POS_STRING; private static final String START_POS_TKEY = "item.possetter.firstpos"; private static final String END_POS_TKEY = "item.possetter.secondpos"; private static final String MISSING_POS_TKEY = "item.possetter.missingpos"; @@ -41,7 +41,7 @@ public abstract class AbstractItemWithPosSelector extends Item */ public AbstractItemWithPosSelector(final Properties properties) { - super(properties.component(ModDataComponents.POS_SELECTION, PosSelection.EMPTY)); + super(properties); } /** @@ -67,37 +67,37 @@ public AbstractItemWithPosSelector(final Properties properties) * {@inheritDoc} */ @Override - public InteractionResultHolder use(final Level worldIn, final Player playerIn, final InteractionHand handIn) + public InteractionResult use(final Level worldIn, final Player playerIn, final InteractionHand handIn) { final ItemStack itemstack = playerIn.getItemInHand(handIn); - final PosSelection compound = PosSelection.readFromItemStack(itemstack); + final CompoundTag compound = ItemStackNbtHelper.getOrCreateCustomTag(itemstack); - if (compound.startPos().isEmpty()) + if (!compound.contains(NBT_START_POS)) { if (worldIn.isClientSide()) { - playerIn.displayClientMessage(Component.translatable(MISSING_POS_TKEY + "1"), false); + playerIn.sendSystemMessage(Component.translatable(MISSING_POS_TKEY + "1")); } - return InteractionResultHolder.fail(itemstack); + return InteractionResult.FAIL; } - if (compound.endPos().isEmpty()) + if (!compound.contains(NBT_END_POS)) { if (worldIn.isClientSide()) { - playerIn.displayClientMessage(Component.translatable(MISSING_POS_TKEY + "2"), false); + playerIn.sendSystemMessage(Component.translatable(MISSING_POS_TKEY + "2")); } - return InteractionResultHolder.fail(itemstack); + return InteractionResult.FAIL; } - return new InteractionResultHolder<>( + final InteractionResult result = onAirRightClick( - compound.startPos().get(), - compound.endPos().get(), + BlockPosUtil.readFromNBT(compound, NBT_START_POS), + BlockPosUtil.readFromNBT(compound, NBT_END_POS), worldIn, playerIn, - itemstack), - itemstack); + itemstack); + return result instanceof final Success success ? success.heldItemTransformedTo(itemstack) : result; } /** @@ -110,10 +110,10 @@ public InteractionResult useOn(final UseOnContext context) final BlockPos pos = context.getClickedPos(); if (context.getLevel().isClientSide()) { - context.getPlayer().displayClientMessage(Component.translatable(END_POS_TKEY, pos.getX(), pos.getY(), pos.getZ()), false); + context.getPlayer().sendSystemMessage(Component.translatable(END_POS_TKEY, pos.getX(), pos.getY(), pos.getZ())); Utils.playSuccessSound(context.getPlayer()); } - PosSelection.updateItemStack(context.getItemInHand(), data -> data.setEndpos(pos)); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(context.getItemInHand()), NBT_END_POS, pos); return InteractionResult.SUCCESS; } @@ -122,18 +122,27 @@ public InteractionResult useOn(final UseOnContext context) * {@inheritDoc} */ @Override - public boolean canAttackBlock(final BlockState state, final Level worldIn, final BlockPos pos, final Player player) + public boolean canDestroyBlock(final ItemStack selectedStack, + final BlockState state, + final Level worldIn, + final BlockPos pos, + final LivingEntity entity) { + if (!(entity instanceof final Player player) || !player.isShiftKeyDown()) + { + return super.canDestroyBlock(selectedStack, state, worldIn, pos, entity); + } + ItemStack itemstack = player.getMainHandItem(); if (!itemstack.getItem().equals(getRegisteredItemInstance())) { itemstack = player.getOffhandItem(); } - PosSelection.updateItemStack(itemstack, data -> data.setStartPos(pos)); - if (player.getCommandSenderWorld().isClientSide()) + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(itemstack), NBT_START_POS, pos); + if (player.level().isClientSide()) { Utils.playSuccessSound(player); - player.displayClientMessage(Component.translatable(START_POS_TKEY, pos.getX(), pos.getY(), pos.getZ()), false); + player.sendSystemMessage(Component.translatable(START_POS_TKEY, pos.getX(), pos.getY(), pos.getZ())); } return false; } @@ -152,105 +161,26 @@ public float getDestroySpeed(final ItemStack stack, final BlockState state) * @param tool The tool stack (assumed already been validated) * @param start The new start position * @param end The new end position - * @deprecated use datacomponents */ - @Deprecated(forRemoval = true, since = "1.21") public static void setBounds(@NotNull final ItemStack tool, @NotNull final BlockPos start, @NotNull final BlockPos end) { - PosSelection.updateItemStack(tool, data -> data.setSelection(start, end)); + final CompoundTag tag = ItemStackNbtHelper.getOrCreateCustomTag(tool); + BlockPosUtil.writeToNBT(tag, NBT_START_POS, start); + BlockPosUtil.writeToNBT(tag, NBT_END_POS, end); } /** * Loads the start/end coordinates from this stack. * @param tool The tool stack (assumed already been validated) * @return the start/end positions - * @deprecated use datacomponents */ - @Deprecated(forRemoval = true, since = "1.21") public static Tuple getBounds(@NotNull final ItemStack tool) { - final PosSelection tag = PosSelection.readFromItemStack(tool); - return new Tuple<>(tag.startPos().orElse(null), tag.endPos().orElse(null)); - } - - /** - * Data components for storing start and end pos - */ - public record PosSelection(Optional startPos, Optional endPos) - { - public static final PosSelection EMPTY = new PosSelection(Optional.empty(), Optional.empty()); - - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockPos.CODEC.optionalFieldOf("start_pos").forGetter(PosSelection::startPos), - BlockPos.CODEC.optionalFieldOf("end_pos").forGetter(PosSelection::endPos)) - .apply(builder, PosSelection::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - PosSelection::startPos, - ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - PosSelection::endPos, - PosSelection::new); - - /** - * @return true if both start and end positions are set - */ - public boolean hasSelection() - { - return startPos.isPresent() && endPos.isPresent(); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public PosSelection setStartPos(final BlockPos pos) - { - return new PosSelection(Optional.ofNullable(pos), endPos); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public PosSelection setEndpos(final BlockPos pos) - { - return new PosSelection(startPos, Optional.ofNullable(pos)); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public PosSelection setSelection(final BlockPos startPos, final BlockPos endPos) - { - return new PosSelection(Optional.ofNullable(startPos), Optional.ofNullable(endPos)); - } - - /** - * Writes this posSelection into given itemStack. - * - * @see BlockEntity#saveToItem(ItemStack, net.minecraft.core.HolderLookup.Provider) - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.POS_SELECTION, this); - } - - /** - * @return posSelection stored in given itemStack (or empty instance) - */ - public static PosSelection readFromItemStack(final ItemStack itemStack) - { - return itemStack.getOrDefault(ModDataComponents.POS_SELECTION, PosSelection.EMPTY); - } - - /** - * Performs updating of posSelection in given itemStack - */ - public static void updateItemStack(final ItemStack itemStack, final UnaryOperator updater) - { - updater.apply(readFromItemStack(itemStack)).writeToItemStack(itemStack); - } + final CompoundTag tag = ItemStackNbtHelper.getOrCreateCustomTag(tool); + final BlockPos start = BlockPosUtil.readFromNBT(tag, NBT_START_POS); + final BlockPos end = BlockPosUtil.readFromNBT(tag, NBT_END_POS); + return new Tuple<>(start, end); } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java b/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java index 0d85d0d6d7..d05681f533 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java @@ -1,18 +1,15 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.client.gui.WindowExtendedBuildTool; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.ItemStackUtils; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionHand; import net.minecraft.world.level.Level; -import static com.ldtteam.structurize.api.constants.Constants.GROUNDSTYLE_RELATIVE; + +import static com.ldtteam.structurize.api.util.constant.Constants.GROUNDSTYLE_RELATIVE; /** import net.minecraft.world.item.Item.Properties; @@ -22,61 +19,35 @@ public class ItemBuildTool extends AbstractItemStructurize { /** * Instantiates the buildTool on load. + * @param properties the properties. */ - public ItemBuildTool() + public ItemBuildTool(final Properties properties) { - super("sceptergold", new Properties().stacksTo(1)); + super("sceptergold", properties.stacksTo(1)); } @Override @SuppressWarnings("resource") public InteractionResult useOn(final UseOnContext context) { - if (context.getLevel().isClientSide) + if (context.getLevel().isClientSide()) { - openBuildToolWindow(context.getClickedPos().relative(context.getClickedFace()), GROUNDSTYLE_RELATIVE, context.getLevel().registryAccess()); + Structurize.proxy.openBuildToolWindow(context.getClickedPos().relative(context.getClickedFace()), GROUNDSTYLE_RELATIVE); } return InteractionResult.SUCCESS; } @Override - public InteractionResultHolder use(final Level worldIn, final Player playerIn, final InteractionHand handIn) + public InteractionResult use(final Level worldIn, final Player playerIn, final InteractionHand handIn) { final ItemStack stack = playerIn.getItemInHand(handIn); - if (worldIn.isClientSide) - { - openBuildToolWindow(null, GROUNDSTYLE_RELATIVE, worldIn.registryAccess()); - } - - return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); - } - - private static void openBuildToolWindow(final BlockPos pos, final int groundstyle, final HolderLookup.Provider provider) - { - if (Minecraft.getInstance().screen != null) + if (worldIn.isClientSide()) { - return; + Structurize.proxy.openBuildToolWindow(null, GROUNDSTYLE_RELATIVE); } - new WindowExtendedBuildTool(pos, groundstyle, null, WindowExtendedBuildTool.BLOCK_BLUEPRINT_REQUIREMENT, provider).open(); - } - - @Override - public ItemStack getCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the build tool when use for crafting - if (ItemStackUtils.isEmpty(itemStack)) - { - return ItemStack.EMPTY; - } - return itemStack.copy(); + return InteractionResult.SUCCESS.heldItemTransformedTo(stack); } - @Override - public boolean hasCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the build tool when use for crafting - return !ItemStackUtils.isEmpty(itemStack); - } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java b/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java index 65ff4def3c..aa3474f6de 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java @@ -19,10 +19,12 @@ public class ItemCaliper extends AbstractItemWithPosSelector /** * Caliper constructor. Sets max stack to 1, like other tools. + * + * @param properties */ - public ItemCaliper() + public ItemCaliper(final Properties properties) { - super(new Properties().stacksTo(1)); + super(properties.stacksTo(1)); } @Override @@ -38,7 +40,7 @@ public InteractionResult onAirRightClick(final BlockPos start, final Player playerIn, final ItemStack itemStack) { - if (!worldIn.isClientSide) + if (!worldIn.isClientSide()) { return InteractionResult.FAIL; } @@ -68,7 +70,7 @@ private void handlePlayerMessage(final BlockPos start, final BlockPos end, final distances.add(disZ + 1); } - playerIn.displayClientMessage(Component.translatable(String.format(ITEM_CALIPER_MESSAGE_XD, distances.size()), - distances.toArray(new Object[0])), false); + playerIn.sendSystemMessage(Component.translatable(String.format(ITEM_CALIPER_MESSAGE_XD, distances.size()), + distances.toArray(new Object[0]))); } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java b/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java index 6a0dacb71f..e70507c741 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java @@ -1,20 +1,21 @@ package com.ldtteam.structurize.items; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.IScrollableItem; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.IScrollableItem; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; import com.ldtteam.structurize.client.gui.WindowScan; +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewRenderTask; import com.ldtteam.structurize.commands.ScanCommand; -import com.ldtteam.structurize.component.ModDataComponents; import com.ldtteam.structurize.network.messages.SaveScanMessage; import com.ldtteam.structurize.network.messages.ShowScanMessage; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.ScanToolData; import com.mojang.brigadier.CommandDispatcher; @@ -30,16 +31,23 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Rarity; +import net.minecraft.world.item.component.TooltipDisplay; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -48,6 +56,7 @@ import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,12 +66,11 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; -import static com.ldtteam.structurize.api.constants.TranslationConstants.ANCHOR_POS_OUTSIDE_SCHEMATIC; -import static com.ldtteam.structurize.api.constants.TranslationConstants.MAX_SCHEMATIC_SIZE_REACHED; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.ANCHOR_POS_OUTSIDE_SCHEMATIC; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.MAX_SCHEMATIC_SIZE_REACHED; import static com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE.TAG_BLUEPRINTDATA; /** @@ -71,16 +79,17 @@ public class ItemScanTool extends AbstractItemWithPosSelector implements IScrollableItem, ISpecialBlockPickItem { private static final String ANCHOR_POS_TKEY = "item.possetter.anchorpos"; + private static final String NBT_ANCHOR_POS = "structurize:anchor_pos"; + private static final String NBT_NAME = "structurize:name"; + private static final String NBT_COMMAND_POS = "structurize:cmd_pos"; + private static final String NBT_DIMENSION = "structurize:dim"; /** * Creates default scan tool item. */ public ItemScanTool() { - this(new Properties().durability(0) - .setNoRepair() - .rarity(Rarity.UNCOMMON) - .component(ModDataComponents.SCAN_TOOL, ScanToolData.EMPTY)); + this(new Item.Properties().durability(0).rarity(Rarity.UNCOMMON)); } /** @@ -96,13 +105,14 @@ public ItemScanTool(final Properties properties) @Override public InteractionResult onAirRightClick(final BlockPos start, final BlockPos end, final Level worldIn, final Player playerIn, final ItemStack itemStack) { - final ScanToolData data = ScanToolData.updateItemStack(itemStack, d -> saveSlot(d, itemStack, playerIn)); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(itemStack)); + saveSlot(data, itemStack, playerIn); - if (!worldIn.isClientSide) + if (!worldIn.isClientSide()) { if (playerIn.isShiftKeyDown()) { - saveStructure(worldIn, playerIn, data.currentSlot(), true); + saveStructure(worldIn, playerIn, data.getCurrentSlotData(), true); } } else @@ -136,30 +146,30 @@ public static void saveStructure( final ScanToolData.Slot slot, final boolean saveEntities) { - if (slot.box().anchor().isPresent()) + if (slot.getBox().getAnchor().isPresent()) { - if (!BlockPosUtil.isInbetween(slot.box().anchor().get(), slot.box().pos1(), slot.box().pos2())) + if (!BlockPosUtil.isInbetween(slot.getBox().getAnchor().get(), slot.getBox().getPos1(), slot.getBox().getPos2())) { - player.displayClientMessage(Component.translatable(ANCHOR_POS_OUTSIDE_SCHEMATIC), false); + player.sendSystemMessage(Component.translatable(ANCHOR_POS_OUTSIDE_SCHEMATIC)); return; } } - final BoundingBox box = BoundingBox.fromCorners(slot.box().pos1(), slot.box().pos2()); + final BoundingBox box = BoundingBox.fromCorners(slot.getBox().getPos1(), slot.getBox().getPos2()); if (box.getXSpan() * box.getYSpan() * box.getZSpan() > Structurize.getConfig().getServer().schematicBlockLimit.get()) { - player.displayClientMessage(Component.translatable(MAX_SCHEMATIC_SIZE_REACHED, Structurize.getConfig().getServer().schematicBlockLimit.get()), false); + player.sendSystemMessage(Component.translatable(MAX_SCHEMATIC_SIZE_REACHED, Structurize.getConfig().getServer().schematicBlockLimit.get())); return; } String fileName; - if (slot.name().isEmpty()) + if (slot.getName().isEmpty()) { fileName = Component.translatable("item.sceptersteel.scanformat", new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(Date.from(Instant.now()))).getString(); } else { - fileName = slot.name(); + fileName = slot.getName(); } if (!fileName.contains(".blueprint")) @@ -168,9 +178,9 @@ public static void saveStructure( } final BlockPos zero = new BlockPos(box.minX(), box.minY(), box.minZ()); - final Blueprint bp = BlueprintUtil.createBlueprint(world, zero, saveEntities, (short) box.getXSpan(), (short) box.getYSpan(), (short) box.getZSpan(), fileName, slot.box().anchor()); + final Blueprint bp = BlueprintUtil.createBlueprint(world, zero, saveEntities, (short) box.getXSpan(), (short) box.getYSpan(), (short) box.getZSpan(), fileName, slot.getBox().getAnchor()); - if (slot.box().anchor().isEmpty() && bp.getPrimaryBlockOffset().equals(new BlockPos(bp.getSizeX() / 2, 0, bp.getSizeZ() / 2))) + if (slot.getBox().getAnchor().isEmpty() && bp.getPrimaryBlockOffset().equals(new BlockPos(bp.getSizeX() / 2, 0, bp.getSizeZ() / 2))) { final List list = bp.getBlockInfoAsList().stream() .filter(blockInfo -> blockInfo.hasTileEntityData() && blockInfo.getTileEntityData().contains(TAG_BLUEPRINTDATA)) @@ -178,24 +188,28 @@ public static void saveStructure( if (list.size() > 1) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.scanbadanchor", fileName), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.scanbadanchor", fileName)); } } - new SaveScanMessage(BlueprintUtil.writeBlueprintToNBT(bp), fileName).sendToPlayer((ServerPlayer) player); + Network.getNetwork().sendToPlayer(new SaveScanMessage(BlueprintUtil.writeBlueprintToNBT(bp), fileName), (ServerPlayer) player); } @Override - public boolean canAttackBlock(final BlockState state, final Level worldIn, final BlockPos pos, final Player player) + public boolean canDestroyBlock(final ItemStack selectedStack, + final BlockState state, + final Level worldIn, + final BlockPos pos, + final LivingEntity entity) { - if (!player.isShiftKeyDown()) + if (!(entity instanceof final Player player) || !player.isShiftKeyDown()) { - return super.canAttackBlock(state, worldIn, pos, player); + return super.canDestroyBlock(selectedStack, state, worldIn, pos, entity); } if (worldIn.isClientSide()) { - player.displayClientMessage(Component.translatable(ANCHOR_POS_TKEY, pos.getX(), pos.getY(), pos.getZ()), false); + player.sendSystemMessage(Component.translatable(ANCHOR_POS_TKEY, pos.getX(), pos.getY(), pos.getZ())); } ItemStack itemstack = player.getMainHandItem(); @@ -205,25 +219,24 @@ public boolean canAttackBlock(final BlockState state, final Level worldIn, final } final BlockEntity te = worldIn.getBlockEntity(pos); - if (te instanceof final IBlueprintDataProviderBE bpProvider && !bpProvider.getSchematicName().isEmpty()) + if (te instanceof IBlueprintDataProviderBE && !((IBlueprintDataProviderBE) te).getSchematicName().isEmpty()) { - final BlockPos start = bpProvider.getInWorldCorners().getA(); - final BlockPos end = bpProvider.getInWorldCorners().getB(); + final BlockPos start = ((IBlueprintDataProviderBE) te).getInWorldCorners().getA(); + final BlockPos end = ((IBlueprintDataProviderBE) te).getInWorldCorners().getB(); if (!(start.equals(pos)) && !(end.equals(pos))) { - if (worldIn.isClientSide) - { - RenderingCache.queue("scan", new BoxPreviewData(bpProvider.getInWorldCorners().getA(), bpProvider.getInWorldCorners().getB(), Optional.of(pos))); - } - PosSelection.updateItemStack(itemstack, data -> data.setSelection(start, end)); + setBounds(itemstack, start, end); } - else + + if (worldIn.isClientSide()) { - if (worldIn.isClientSide && RenderingCache.getBoxPreviewData("scan") != null) - { - RenderingCache.queue("scan", RenderingCache.getBoxPreviewData("scan").withAnchor(Optional.of(pos))); - } + RenderTaskManager.addRenderTask("scan", + new BoxPreviewRenderTask("scan", + new BoxPreviewData(((IBlueprintDataProviderBE) te).getInWorldCorners().getA(), + ((IBlueprintDataProviderBE) te).getInWorldCorners().getB(), + Optional.of(pos)), + 60 * 10)); } } @@ -233,15 +246,16 @@ public boolean canAttackBlock(final BlockState state, final Level worldIn, final @Override public void appendHoverText(@NotNull ItemStack stack, - @Nullable TooltipContext world, - @NotNull List tooltip, + @Nullable Item.TooltipContext world, + @NotNull TooltipDisplay display, + @NotNull Consumer tooltip, @NotNull TooltipFlag flags) { - super.appendHoverText(stack, world, tooltip, flags); + super.appendHoverText(stack, world, display, tooltip, flags); - if (stack.has(ModDataComponents.SCAN_TOOL)) + if (ItemStackNbtHelper.hasCustomTag(stack)) { - tooltip.add(getCurrentSlotDescription(stack)); + tooltip.accept(getCurrentSlotDescription(stack)); } } @@ -256,11 +270,11 @@ public Component getHighlightTip(@NotNull final ItemStack stack, @NotNull final private Component getCurrentSlotDescription(@NotNull final ItemStack stack) { - final ScanToolData data = ScanToolData.readFromItemStack(stack); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); MutableComponent desc = Component.empty() - .append(Component.literal(Integer.toString(data.currentSlotId())).withStyle(ChatFormatting.GRAY)); + .append(Component.literal(String.valueOf(data.getCurrentSlotId())).withStyle(ChatFormatting.GRAY)); - final String name = data.currentSlot().name(); + final String name = getStructureName(stack); if (!name.isEmpty()) { desc = desc.append(Component.literal(": ").withStyle(ChatFormatting.GRAY)) @@ -297,52 +311,53 @@ public InteractionResult onBlockPick(@NotNull final Player player, @Override public InteractionResult onMouseScroll(@NotNull final Player player, @NotNull final ItemStack stack, - final double deltaX, - final double deltaY, + final double delta, final boolean ctrlKey) { if (!player.level().isClientSide() || Structurize.getConfig().getClient().scanToolScrolling.get()) { - return switchSlot(player, stack, deltaY < 0 ? ScanToolData::prevSlot : ScanToolData::nextSlot); + return switchSlot(player, stack, delta < 0 ? ScanToolData::prevSlot : ScanToolData::nextSlot); } return InteractionResult.PASS; } @NotNull - private InteractionResult switchSlot( - @NotNull final Player player, - @NotNull final ItemStack stack, - @NotNull final UnaryOperator action) + private InteractionResult switchSlot(@NotNull final Player player, + @NotNull final ItemStack stack, + @NotNull final Consumer action) { if (player.level().isClientSide()) { return InteractionResult.SUCCESS; } - final ScanToolData data = ScanToolData.updateItemStack(stack, d -> action.apply(saveSlot(d, stack, player))); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); + saveSlot(data, stack, player); + action.accept(data); final ScanToolData.Slot slot = loadSlot(data, stack); - new ShowScanMessage(slot.box()).sendToPlayer((ServerPlayer) player); + Network.getNetwork().sendToPlayer(new ShowScanMessage(slot.getBox()), (ServerPlayer) player); return InteractionResult.SUCCESS; } - private ScanToolData saveSlot(@NotNull final ScanToolData data, - @NotNull final ItemStack stack, - @NotNull final Player player) + private void saveSlot(@NotNull final ScanToolData data, + @NotNull final ItemStack stack, + @NotNull final Player player) { - final BoxPreviewData box = getBox(stack, player); - return data.withCurrentSlot(box == null ? null : data.currentSlot().withBox(box)); + data.setCurrentSlotData(new ScanToolData.Slot(getStructureName(stack), getBox(stack, player))); } public ScanToolData.Slot loadSlot(@NotNull final ScanToolData data, @NotNull final ItemStack stack) { - final ScanToolData.Slot slot = data.currentSlot(); + final ScanToolData.Slot slot = data.getCurrentSlotData(); // this seems a little silly at first, duplicating this info outside the slot storage. // but it preserves compatibility with AbstractItemWithPosSelector. - PosSelection.updateItemStack(stack, data1 -> data1.setSelection(slot.box().pos1(), slot.box().pos2())); + setStructureName(stack, slot.getName()); + setBounds(stack, slot.getBox().getPos1(), slot.getBox().getPos2()); + setAnchorPos(stack, slot.getBox().getAnchor().orElse(null)); return slot; } @@ -396,7 +411,9 @@ private void onCommandBlockCopy(@NotNull final ServerPlayer player, if (reader.canRead() && reader.peek() == '/') { reader.read(); } final CommandDispatcher dispatcher = player.level().getServer().getCommands().getDispatcher(); - final ParseResults parsed = dispatcher.parse(reader, command.getCommandBlock().createCommandSourceStack()); + final ParseResults parsed = dispatcher.parse( + reader, + command.getCommandBlock().createCommandSourceStack((ServerLevel) player.level(), CommandSource.NULL)); if (parsed.getReader().canRead() || parsed.getContext().getNodes().size() < 4 || !parsed.getContext().getNodes().get(0).getNode().getName().equals(MOD_ID) || !parsed.getContext().getNodes().get(1).getNode().getName().equals(ScanCommand.NAME)) @@ -410,36 +427,31 @@ private void onCommandBlockCopy(@NotNull final ServerPlayer player, { final BlockPos from = BlockPosArgument.getSpawnablePos(cmdContext, ScanCommand.POS1); final BlockPos to = BlockPosArgument.getSpawnablePos(cmdContext, ScanCommand.POS2); - final Optional anchor; + Optional anchor = Optional.empty(); if (parsed.getContext().getArguments().containsKey(ScanCommand.ANCHOR_POS)) { anchor = Optional.of(BlockPosArgument.getSpawnablePos(cmdContext, ScanCommand.ANCHOR_POS)); } - else - { - anchor = Optional.empty(); - } - final String name; + String name = ""; if (parsed.getContext().getArguments().containsKey(ScanCommand.FILE_NAME)) { name = StringArgumentType.getString(cmdContext, ScanCommand.FILE_NAME); } - else - { - name = ""; - } - final ScanToolData data = ScanToolData.updateItemStack(stack, d -> d.withCommandBlock(command) - .withCurrentSlot(new ScanToolData.Slot(name, new BoxPreviewData(from, to, anchor)))); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(stack), NBT_COMMAND_POS, command.getBlockPos()); + ItemStackNbtHelper.getOrCreateCustomTag(stack).putString(NBT_DIMENSION, command.getLevel().dimension().identifier().toString()); + + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); + data.setCurrentSlotData(new ScanToolData.Slot(name, new BoxPreviewData(from, to, anchor))); final ScanToolData.Slot slot = loadSlot(data, stack); - new ShowScanMessage(slot.box()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new ShowScanMessage(slot.getBox()), player); - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.ok", name), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_CHIME.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.ok", name)); + player.playSound(SoundEvents.NOTE_BLOCK_CHIME.value(), 1.0F, 1.0F); } catch (CommandSyntaxException e) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.notscan"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.notscan")); } } @@ -455,13 +467,14 @@ private void onCommandBlockPaste(@NotNull final ServerPlayer player, @NotNull final CommandBlockEntity command, final boolean ctrlKey) { - final ScanToolData data = ScanToolData.updateItemStack(stack, d -> saveSlot(d, stack, player)); - final ScanToolData.Slot slot = data.currentSlot(); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); + saveSlot(data, stack, player); + final ScanToolData.Slot slot = data.getCurrentSlotData(); - if (slot.name().isBlank() || slot.name().contains(" ")) + if (slot.getName().isBlank() || slot.getName().contains(" ")) { player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.badname")); - player.playNotifySound(SoundEvents.NOTE_BLOCK_BIT.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); return; } @@ -472,8 +485,8 @@ private void onCommandBlockPaste(@NotNull final ServerPlayer player, } else if (!command.getCommandBlock().getCommand().contains(MOD_ID + " " + ScanCommand.NAME + " ")) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.badcommand"), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_BIT.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.badcommand")); + player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); return; } else if (!ctrlKey) @@ -481,16 +494,18 @@ else if (!ctrlKey) final StringReader reader = new StringReader(command.getCommandBlock().getCommand()); if (reader.canRead() && reader.peek() == '/') { reader.read(); } - final CommandDispatcher dispatcher = player.getServer().getCommands().getDispatcher(); - final ParseResults parsed = dispatcher.parse(reader, command.getCommandBlock().createCommandSourceStack()); + final CommandDispatcher dispatcher = player.level().getServer().getCommands().getDispatcher(); + final ParseResults parsed = dispatcher.parse( + reader, + command.getCommandBlock().createCommandSourceStack((ServerLevel) player.level(), CommandSource.NULL)); if (parsed.getContext().getArguments().containsKey(ScanCommand.FILE_NAME)) { final CommandContext cmdContext = parsed.getContext().build(parsed.getReader().getString()); final String currentName = StringArgumentType.getString(cmdContext, ScanCommand.FILE_NAME); - if (!currentName.equals(slot.name())) + if (!currentName.equals(slot.getName())) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.different", slot.name(), currentName), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_XYLOPHONE.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.different", slot.getName(), currentName)); + player.playSound(SoundEvents.NOTE_BLOCK_XYLOPHONE.value(), 1.0F, 1.0F); return; } } @@ -499,10 +514,11 @@ else if (!ctrlKey) final String cmd = ScanCommand.format(slot); command.getCommandBlock().setCommand(cmd); - ScanToolData.updateItemStack(stack, d -> d.withCommandBlock(command)); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(stack), NBT_COMMAND_POS, command.getBlockPos()); + ItemStackNbtHelper.getOrCreateCustomTag(stack).putString(NBT_DIMENSION, command.getLevel().dimension().identifier().toString()); - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.ok", slot.name()), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_CHIME.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.ok", slot.getName())); + player.playSound(SoundEvents.NOTE_BLOCK_CHIME.value(), 1.0F, 1.0F); } /** @@ -518,40 +534,41 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack return false; } - final ScanToolData data = ScanToolData.readFromItemStack(stack); - if (data.commandPos() == null) + if (ItemStackNbtHelper.getCustomTag(stack) == null || !ItemStackNbtHelper.getCustomTag(stack).contains(NBT_COMMAND_POS)) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.nocmd"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.nocmd")); player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); } return false; } - if (!player.level().dimension().equals(data.dimension())) + if (!player.level().dimension().identifier().toString().equals(ItemStackNbtHelper.getCustomTag(stack).getString(NBT_DIMENSION))) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.dimension"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.dimension")); player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); } return false; } - final ScanToolData.Slot slot = data.currentSlot(); - if (slot == null) + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getCustomTag(stack)); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + + if (slot.getBox().getPos1().equals(BlockPos.ZERO) && slot.getBox().getPos2().equals(BlockPos.ZERO)) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.noscan"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.noscan")); player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); } return false; } - final BlockPos commandPos = data.commandPos().above(); - final BlockPos buildPos = getTeleportPos(slot.box()); + final BlockPos commandPos = BlockPosUtil.readFromNBT(ItemStackNbtHelper.getOrCreateCustomTag(stack), NBT_COMMAND_POS).above(); + final BlockPos buildPos = getTeleportPos(slot.getBox()); final Level level = player.level(); final long commandDistance = BlockPosUtil.getDistanceSquared(commandPos, player.blockPosition()); @@ -572,7 +589,7 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack target = safeTarget; } - if (target.getY() < level.getMinBuildHeight() + 2) + if (target.getY() < level.getMinY() + 2) { // safety abort if we would teleport to bedrock or below (which can happen if the heightmap check fails) Log.getLogger().warn("Aborting attempt to scan-teleport " + player.getName().getString() + " to " + target.toShortString()); @@ -589,7 +606,7 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack { player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); - final CommandSourceStack source = new CommandSourceStack(CommandSource.NULL, player.position(), Vec2.ZERO, serverLevel, 2, + final CommandSourceStack source = new CommandSourceStack(CommandSource.NULL, player.position(), Vec2.ZERO, serverLevel, PermissionSet.ALL_PERMISSIONS, player.getName().getString(), stack.getDisplayName(), serverLevel.getServer(), player); final CommandDispatcher dispatcher = serverLevel.getServer().getCommands().getDispatcher(); try @@ -602,7 +619,7 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack } player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); - player.playNotifySound(SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1.0F, 1.0F); + player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); } return true; } @@ -623,7 +640,7 @@ private BlockPos getTeleportPos(@NotNull final BoxPreviewData box) final Direction direction = Structurize.getConfig().getServer().teleportBuildDirection.get(); final int offset = Structurize.getConfig().getServer().teleportBuildDistance.get(); - final AABB bounds = AABB.encapsulatingFullBlocks(box.pos1(), box.pos2()); + final AABB bounds = new AABB(Vec3.atLowerCornerOf(box.getPos1()), Vec3.atLowerCornerOf(box.getPos2())); final int size = (int) Math.round(bounds.max(direction.getAxis()) - bounds.min(direction.getAxis())); return BlockPos.containing(bounds.getCenter()).atY((int) bounds.minY).relative(direction, offset + size / 2); @@ -635,24 +652,19 @@ private BlockPos getTeleportPos(@NotNull final BoxPreviewData box) * @param player The player who will be notified if it has a bad anchor position * @return the box */ - @Nullable public static BoxPreviewData getBox(@NotNull final ItemStack tool, @NotNull final Player player) { - final PosSelection tag = PosSelection.readFromItemStack(tool); - if (!tag.hasSelection()) - { - return null; - } - Optional anchor = ScanToolData.readFromItemStack(tool).currentSlot().box().anchor(); - if (anchor.isPresent() && !BlockPosUtil.isInbetween(anchor.get(), tag.startPos().get(), tag.endPos().get())) + final Tuple bounds = getBounds(tool); + Optional anchor = Optional.ofNullable(getAnchorPos(tool)); + if (anchor.isPresent() && !BlockPosUtil.isInbetween(anchor.get(), bounds.getA(), bounds.getB())) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.outsideanchor"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.outsideanchor")); } anchor = Optional.empty(); } - return new BoxPreviewData(tag.startPos().get(), tag.endPos().get(), anchor); + return new BoxPreviewData(bounds.getA(), bounds.getB(), anchor); } /** @@ -660,16 +672,17 @@ public static BoxPreviewData getBox(@NotNull final ItemStack tool, @NotNull fina * @param tool The tool stack (assumed already been validated) * @param anchor The new anchor position (or null to clear) */ - @Deprecated(forRemoval = true, since = "1.21") public static void setAnchorPos(@NotNull final ItemStack tool, @Nullable final BlockPos anchor) { - ScanToolData.updateItemStack(tool, data -> + if (anchor == null) + { + ItemStackNbtHelper.getOrCreateCustomTag(tool).remove(NBT_ANCHOR_POS); + } + else { - final BoxPreviewData oldBox = data.currentSlot().box(); - final BoxPreviewData newBox = oldBox.withAnchor(Optional.ofNullable(anchor)); - return data.withCurrentSlot(data.currentSlot().withBox(newBox)); - }); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(tool), NBT_ANCHOR_POS, anchor); + } } /** @@ -678,10 +691,10 @@ public static void setAnchorPos(@NotNull final ItemStack tool, * @return the anchor position or null */ @Nullable - @Deprecated(forRemoval = true, since = "1.21") public static BlockPos getAnchorPos(@NotNull final ItemStack tool) { - return ScanToolData.readFromItemStack(tool).currentSlot().box().anchor().orElse(null); + final CompoundTag tag = ItemStackNbtHelper.getOrCreateCustomTag(tool); + return tag.contains(NBT_ANCHOR_POS) ? BlockPosUtil.readFromNBT(tag, NBT_ANCHOR_POS) : null; } /** @@ -689,11 +702,17 @@ public static BlockPos getAnchorPos(@NotNull final ItemStack tool) * @param tool The tool stack (assumed already validated) * @param name The structure name (or null/empty to clear) */ - @Deprecated(forRemoval = true, since = "1.21") public static void setStructureName(@NotNull final ItemStack tool, - @Nullable String name) + @Nullable final String name) { - ScanToolData.updateItemStack(tool, data -> data.withCurrentSlot(data.currentSlot().withName(name == null ? "" : name))); + if (name == null || name.isEmpty()) + { + ItemStackNbtHelper.getOrCreateCustomTag(tool).remove(NBT_NAME); + } + else + { + ItemStackNbtHelper.getOrCreateCustomTag(tool).putString(NBT_NAME, name); + } } /** @@ -701,9 +720,8 @@ public static void setStructureName(@NotNull final ItemStack tool, * @param tool The tool stack (assumed already validated) * @return The structure name (or empty string) */ - @Deprecated(forRemoval = true, since = "1.21") public static String getStructureName(@NotNull final ItemStack tool) { - return ScanToolData.readFromItemStack(tool).currentSlot().name(); + return ItemStackNbtHelper.getOrCreateCustomTag(tool).getStringOr(NBT_NAME, ""); } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java b/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java index fcd73f1c6e..796598ccfd 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java @@ -1,11 +1,10 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.client.gui.WindowShapeTool; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.ItemStackUtils; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionHand; import net.minecraft.world.level.Level; @@ -14,53 +13,37 @@ public class ItemShapeTool extends AbstractItemStructurize { /** * Sets the name, creative tab, and registers the item. + * @param properties the properties */ - public ItemShapeTool() + public ItemShapeTool(final Properties properties) { - super("shapetool", new Properties().stacksTo(1)); + super("shapetool", properties.stacksTo(1)); } @Override @SuppressWarnings("resource") public InteractionResult useOn(final UseOnContext context) { - if (context.getLevel().isClientSide) + if (context.getLevel().isClientSide()) { - new WindowShapeTool(context.getClickedPos().relative(context.getClickedFace()), context.getLevel().registryAccess()).open(); + Structurize.proxy.openShapeToolWindow(context.getClickedPos().relative(context.getClickedFace())); } return InteractionResult.SUCCESS; } @Override - public InteractionResultHolder use(final Level worldIn, final Player playerIn, final InteractionHand hand) + public InteractionResult use(final Level worldIn, final Player playerIn, final InteractionHand hand) { final ItemStack stack = playerIn.getItemInHand(hand); - if (worldIn.isClientSide) + if (worldIn.isClientSide()) { - new WindowShapeTool(null, worldIn.registryAccess()).open(); + Structurize.proxy.openShapeToolWindow(null); } - return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); + return InteractionResult.SUCCESS.heldItemTransformedTo(stack); } - @Override - public ItemStack getCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the shape tool when use for crafting - if (ItemStackUtils.isEmpty(itemStack)) - { - return ItemStack.EMPTY; - } - return itemStack.copy(); - } - - @Override - public boolean hasCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the shape tool when use for crafting - return !ItemStackUtils.isEmpty(itemStack); - } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java b/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java index d2b2bc339e..bb96f62643 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java @@ -1,18 +1,20 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.Utils; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.Utils; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; +import com.ldtteam.structurize.blockentities.ModBlockEntities; import com.ldtteam.structurize.blocks.ModBlocks; -import com.ldtteam.structurize.component.CapturedBlock; -import com.ldtteam.structurize.component.ModDataComponents; +import com.ldtteam.structurize.client.TagSubstitutionRenderer; import com.ldtteam.structurize.network.messages.AbsorbBlockMessage; import com.ldtteam.structurize.tag.ModTags; -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderSet; -import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.BlockTags; @@ -21,19 +23,24 @@ import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.component.TypedEntityData; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; +import java.util.Objects; import java.util.Optional; public class ItemTagSubstitution extends BlockItem implements ISpecialBlockPickItem { - public ItemTagSubstitution() + public ItemTagSubstitution(@NotNull final Properties properties) { - super(ModBlocks.blockTagSubstitution.get(), new Properties().component(ModDataComponents.CAPTURED_BLOCK, CapturedBlock.EMPTY)); + super(ModBlocks.blockTagSubstitution.get(), properties); } @NotNull @@ -47,7 +54,7 @@ public InteractionResult onBlockPick(@NotNull Player player, { if (!player.level().isClientSide()) { - CapturedBlock.EMPTY.writeToItemStack(stack); + clearAbsorbedBlock(stack); } return InteractionResult.SUCCESS; } @@ -58,26 +65,35 @@ public InteractionResult onBlockPick(@NotNull Player player, // this way lies madness, and/or Sparta... if (!player.level().isClientSide()) { - CapturedBlock.EMPTY.writeToItemStack(stack); + clearAbsorbedBlock(stack); } return InteractionResult.SUCCESS; } if (player.level().isClientSide()) { - ItemStack pick = getPickedBlock(player, pos, blockstate); + ItemStack pick = getPickedBlock(player, pos, blockstate, ctrlKey); // sadly we can't use the default message since we want to pass an extra ItemStack... // (and getCloneItemStack is client-side-only, somewhat strangely) - new AbsorbBlockMessage(pos, pick).sendToServer(); + Network.getNetwork().sendToServer(new AbsorbBlockMessage(pos, pick)); } return InteractionResult.FAIL; } @NotNull - private ItemStack getPickedBlock(@NotNull Player player, @NotNull BlockPos pos, @NotNull BlockState blockstate) + private ItemStack getPickedBlock(@NotNull Player player, + @NotNull BlockPos pos, + @NotNull BlockState blockstate, + final boolean includeNonCreative) + { + return blockstate.getCloneItemStack(pos, player.level(), includeNonCreative, player); + } + + private void clearAbsorbedBlock(@NotNull ItemStack stack) { - return blockstate.getCloneItemStack(Minecraft.getInstance().hitResult, player.level(), pos, player); + final TagValueOutput output = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING); + setBlockEntityData(stack, ModBlockEntities.TAG_SUBSTITUTION.get(), output); } public void onAbsorbBlock(@NotNull final ServerPlayer player, @@ -88,7 +104,7 @@ public void onAbsorbBlock(@NotNull final ServerPlayer player, final BlockState blockstate = player.level().getBlockState(pos); final BlockEntity blockentity = player.level().getBlockEntity(pos); - final CapturedBlock replacement; + final BlockEntityTagSubstitution.ReplacementBlock replacement; if (blockentity instanceof BlockEntityTagSubstitution blockception) { replacement = blockception.getReplacement(); @@ -100,30 +116,46 @@ else if (!isAllowed(blockentity)) } else { - replacement = new CapturedBlock(blockstate, blockentity, player.level().registryAccess(), absorbItem); + replacement = new BlockEntityTagSubstitution.ReplacementBlock(blockstate, blockentity, absorbItem); } - replacement.writeToItemStack(stack); + final TagValueOutput output = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING); + output.store(replacement.write(new CompoundTag())); + setBlockEntityData(stack, ModBlockEntities.TAG_SUBSTITUTION.get(), output); } private boolean isAllowed(@Nullable final BlockEntity blockentity) { if (blockentity == null) return true; - final HolderSet.Named> tag = BuiltInRegistries.BLOCK_ENTITY_TYPE.getTag(ModTags.SUBSTITUTION_ABSORB_WHITELIST).get(); - return tag.contains(blockentity.getType().builtInRegistryHolder()); + return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(ModTags.SUBSTITUTION_ABSORB_WHITELIST) + .map(tag -> tag.stream().anyMatch(holder -> holder.value() == blockentity.getType())) + .orElse(false); + } + + /** + * Gets the absorbed replacement block from the stack. + * @param stack the stack + * @return the replacement block data (without loading blockentity) + */ + @NotNull + public BlockEntityTagSubstitution.ReplacementBlock getAbsorbedBlock(@NotNull ItemStack stack) + { + final TypedEntityData> blockEntityData = stack.get(DataComponents.BLOCK_ENTITY_DATA); + final CompoundTag tag = Objects.requireNonNullElse(blockEntityData == null ? null : blockEntityData.getUnsafe(), new CompoundTag()); + return new BlockEntityTagSubstitution.ReplacementBlock(tag); } @Override public Component getHighlightTip(@NotNull final ItemStack stack, @NotNull final Component displayName) { - final ItemStack absorbed = CapturedBlock.readFromItemStack(stack).itemStack(); + final BlockEntityTagSubstitution.ReplacementBlock absorbed = getAbsorbedBlock(stack); if (!absorbed.isEmpty()) { return Component.empty() .append(super.getHighlightTip(stack, displayName)) .append(Component.literal(" - ").withStyle(ChatFormatting.GRAY)) - .append(absorbed.getHoverName()); + .append(absorbed.getItemStack().getHoverName()); } return super.getHighlightTip(stack, displayName); @@ -133,7 +165,8 @@ public Component getHighlightTip(@NotNull final ItemStack stack, @NotNull final @Override public Optional getTooltipImage(@NotNull final ItemStack stack) { - final ItemStack absorbedItem = CapturedBlock.readFromItemStack(stack).itemStack(); + final BlockEntityTagSubstitution.ReplacementBlock absorbed = getAbsorbedBlock(stack); + final ItemStack absorbedItem = absorbed.getItemStack(); if (!absorbedItem.isEmpty()) { @@ -142,4 +175,11 @@ public Optional getTooltipImage(@NotNull final ItemStack stack return super.getTooltipImage(stack); } -} + + @Nullable + @Override + protected BlockState getPlacementState(@NotNull final BlockPlaceContext context) + { + return super.getPlacementState(context); + } +} diff --git a/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java b/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java index d547b752d5..0896645e8f 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java @@ -1,20 +1,18 @@ package com.ldtteam.structurize.items; +import com.ldtteam.structurize.api.util.BlockPosUtil; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.client.gui.WindowTagTool; -import com.ldtteam.structurize.component.ModDataComponents; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.InteractionResult.Success; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Rarity; import net.minecraft.world.item.context.UseOnContext; @@ -24,23 +22,21 @@ import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.function.UnaryOperator; /** * Item for tagging positions with tags */ public class ItemTagTool extends AbstractItemWithPosSelector { + public static final String TAG_ANCHOR_POS = "anchorpostag"; + public static final String TAG_CURRENT_TAG = "currenttag"; + /** * Creates default scan tool item. */ public ItemTagTool() { - this(new Properties().durability(0) - .setNoRepair() - .rarity(Rarity.UNCOMMON) - .component(ModDataComponents.TAGS_DATA, TagData.EMPTY)); + this(new Item.Properties().durability(0).rarity(Rarity.UNCOMMON)); } /** @@ -62,32 +58,66 @@ public AbstractItemWithPosSelector getRegisteredItemInstance() @Override public InteractionResult onAirRightClick(final BlockPos start, final BlockPos end, final Level worldIn, final Player playerIn, final ItemStack itemStack) { - if (worldIn.isClientSide) + if (worldIn.isClientSide()) { - final TagData tagData = TagData.readFromItemStack(itemStack); - if (tagData.anchorPos().isEmpty()) + final BlockPos anchorPos = getAnchorPos(itemStack); + if (anchorPos == null) { - playerIn.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor"), false); + playerIn.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor")); return InteractionResult.FAIL; } - final WindowTagTool window = new WindowTagTool(tagData.currentTag().orElse(""), tagData.anchorPos().get(), worldIn, itemStack); + final WindowTagTool window = new WindowTagTool(getCurrentTag(itemStack), anchorPos, worldIn, itemStack); window.open(); } return InteractionResult.SUCCESS; } + /** + * Get the anchor pos from nbt + * + * @param stack stack to use + * @return pos of anchor + */ + private BlockPos getAnchorPos(final ItemStack stack) + { + final CompoundTag itemCompound = ItemStackNbtHelper.getOrCreateCustomTag(stack); + + if (itemCompound.contains(TAG_ANCHOR_POS)) + { + return BlockPosUtil.readFromNBT(itemCompound, TAG_ANCHOR_POS); + } + + return null; + } + + /** + * Getsthe current tag from nbt + * + * @param stack stack to use + * @return tag string + */ + private String getCurrentTag(final ItemStack stack) + { + if (ItemStackNbtHelper.getOrCreateCustomTag(stack).contains(TAG_CURRENT_TAG)) + { + return ItemStackNbtHelper.getOrCreateCustomTag(stack).getStringOr(TAG_CURRENT_TAG, ""); + } + return ""; + } + @Override - public InteractionResultHolder use(Level worldIn, Player playerIn, InteractionHand handIn) + public InteractionResult use(Level worldIn, Player playerIn, InteractionHand handIn) { - return new InteractionResultHolder<>( - onAirRightClick( + final InteractionResult result = onAirRightClick( null, null, worldIn, playerIn, - playerIn.getItemInHand(handIn)), - playerIn.getItemInHand(handIn)); + playerIn.getItemInHand(handIn)); + return result instanceof final Success success + ? success.heldItemTransformedTo(playerIn.getItemInHand(handIn)) + : result; } @Override @@ -104,10 +134,10 @@ public InteractionResult useOn(final UseOnContext context) BlockEntity te = context.getLevel().getBlockEntity(context.getClickedPos()); if (te instanceof IBlueprintDataProviderBE) { - TagData.updateItemStack(context.getItemInHand(), tags -> tags.setAnchorPos(context.getClickedPos())); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(context.getItemInHand()), TAG_ANCHOR_POS, context.getClickedPos()); if (context.getLevel().isClientSide()) { - context.getPlayer().displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchorsaved"), false); + context.getPlayer().sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchorsaved")); } return InteractionResult.SUCCESS; } @@ -115,7 +145,7 @@ public InteractionResult useOn(final UseOnContext context) { if (context.getLevel().isClientSide()) { - context.getPlayer().displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid"), false); + context.getPlayer().sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid")); } return InteractionResult.FAIL; } @@ -125,128 +155,73 @@ public InteractionResult useOn(final UseOnContext context) } @Override - public boolean canAttackBlock(final BlockState state, final Level worldIn, final BlockPos pos, final Player player) + public boolean canDestroyBlock(final ItemStack selectedStack, + final BlockState state, + final Level worldIn, + final BlockPos pos, + final LivingEntity entity) { + if (!(entity instanceof final Player player)) + { + return super.canDestroyBlock(selectedStack, state, worldIn, pos, entity); + } + final ItemStack stack = player.getMainHandItem(); if (stack.getItem() != ModItems.tagTool.get()) { return false; } - final TagData tagData = TagData.readFromItemStack(stack); + BlockPos anchorPos = getAnchorPos(stack); + String currentTag = getCurrentTag(stack); - if (tagData.anchorPos().isEmpty()) + if (anchorPos == null) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor")); return false; } - if (tagData.currentTag().isEmpty()) + if (currentTag.isEmpty()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.notag"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.notag")); return false; } // Apply tag to item - final BlockPos anchorPos = tagData.anchorPos().get(); - final String currentTag = tagData.currentTag().get(); BlockPos relativePos = pos.subtract(anchorPos); final BlockEntity te = worldIn.getBlockEntity(anchorPos); - if (!(te instanceof final IBlueprintDataProviderBE blueprintBe)) + if (!(te instanceof IBlueprintDataProviderBE)) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid"), false); - TagData.updateItemStack(stack, tags -> tags.setAnchorPos(null)); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid")); + ItemStackNbtHelper.getOrCreateCustomTag(stack).remove(TAG_ANCHOR_POS); return false; } // add/remove tags - Map> tagPosMap = blueprintBe.getPositionedTags(); + Map> tagPosMap = ((IBlueprintDataProviderBE) te).getPositionedTags(); if (!tagPosMap.containsKey(relativePos) || !tagPosMap.get(relativePos).contains(currentTag)) { - blueprintBe.addTag(relativePos, currentTag); + ((IBlueprintDataProviderBE) te).addTag(relativePos, currentTag); if (worldIn.isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.addtag", + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.addtag", currentTag, - worldIn.getBlockState(pos).getBlock().getName()), false); + worldIn.getBlockState(pos).getBlock().getName())); } } else { - blueprintBe.removeTag(relativePos, currentTag); + ((IBlueprintDataProviderBE) te).removeTag(relativePos, currentTag); if (worldIn.isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.removed", + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.removed", currentTag, - worldIn.getBlockState(pos).getBlock().getName()), false); + worldIn.getBlockState(pos).getBlock().getName())); } } return false; } - - /** - * Data components for storing start and end pos - */ - public record TagData(Optional anchorPos, Optional currentTag) - { - public static final TagData EMPTY = new TagData(Optional.empty(), Optional.empty()); - - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockPos.CODEC.optionalFieldOf("anchor_pos_tag").forGetter(TagData::anchorPos), - Codec.STRING.optionalFieldOf("current_tag").forGetter(TagData::currentTag)) - .apply(builder, TagData::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - TagData::anchorPos, - ByteBufCodecs.optional(ByteBufCodecs.STRING_UTF8), - TagData::currentTag, - TagData::new); - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public TagData setAnchorPos(final BlockPos pos) - { - return new TagData(Optional.ofNullable(pos), currentTag); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public TagData setCurrentTag(final String currentTag) - { - return new TagData(anchorPos, Optional.ofNullable(currentTag.isEmpty() ? null : currentTag)); - } - - /** - * Writes this tagData into given itemStack. - * - * @see BlockEntity#saveToItem(ItemStack, net.minecraft.core.HolderLookup.Provider) - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.TAGS_DATA, this); - } - - /** - * @return tagData stored in given itemStack (or empty instance) - */ - public static TagData readFromItemStack(final ItemStack itemStack) - { - return itemStack.getOrDefault(ModDataComponents.TAGS_DATA, TagData.EMPTY); - } - - /** - * Performs updating of tagData in given itemStack - */ - public static void updateItemStack(final ItemStack itemStack, final UnaryOperator updater) - { - updater.apply(readFromItemStack(itemStack)).writeToItemStack(itemStack); - } - } } diff --git a/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java b/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java index 722194e3bc..5fa691356a 100644 --- a/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java +++ b/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java @@ -1,35 +1,47 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.blocks.ModBlocks; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; -import java.util.function.Supplier; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * Class used to handle the creativeTab of structurize. */ public final class ModItemGroups { - public static final DeferredRegister TAB_REG = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, Constants.MOD_ID); + public static final DeferredRegister TAB_REG = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MOD_ID); - public static final Supplier GENERAL = TAB_REG.register("general", () -> new CreativeModeTab.Builder(CreativeModeTab.Row.TOP, 1).icon(() -> new ItemStack(ModItems.buildTool.get())).title(Component.translatable("itemGroup." + Constants.MOD_ID)).displayItems((config, output) -> { - output.accept(ModBlocks.blockSubstitution.get()); - output.accept(ModBlocks.blockSolidSubstitution.get()); - output.accept(ModBlocks.blockFluidSubstitution.get()); + public static final DeferredHolder GENERAL = TAB_REG.register("general", () -> new CreativeModeTab.Builder(CreativeModeTab.Row.TOP, 1).icon(() -> new ItemStack(ModItems.buildTool.get())).title(Component.translatable("itemGroup." + MOD_ID)).displayItems((config, output) -> { + // NeoForge 26.2 validates creative-tab entries as single-item stacks. + // Construct the stacks explicitly so custom item max-stack settings do + // not leak into the tab callback. + output.accept(single(ModBlocks.blockSubstitution.get())); + output.accept(single(ModBlocks.blockSolidSubstitution.get())); + output.accept(single(ModBlocks.blockFluidSubstitution.get())); - output.accept(ModItems.buildTool.get()); - output.accept(ModItems.shapeTool.get()); - output.accept(ModItems.scanTool.get()); - output.accept(ModItems.tagTool.get()); - output.accept(ModItems.caliper.get()); - output.accept(ModItems.blockTagSubstitution.get()); + output.accept(single(ModItems.buildTool.get())); + output.accept(single(ModItems.shapeTool.get())); + output.accept(single(ModItems.scanTool.get())); + output.accept(single(ModItems.tagTool.get())); + output.accept(single(ModItems.caliper.get())); + output.accept(single(ModItems.blockTagSubstitution.get())); }).build()); + private static ItemStack single(final ItemLike item) + { + final ItemStack stack = new ItemStack(item); + stack.setCount(1); + return stack; + } + /** * Private constructor to hide the implicit one. */ diff --git a/src/main/java/com/ldtteam/structurize/items/ModItems.java b/src/main/java/com/ldtteam/structurize/items/ModItems.java index 6efaa8947b..12bb46d1aa 100644 --- a/src/main/java/com/ldtteam/structurize/items/ModItems.java +++ b/src/main/java/com/ldtteam/structurize/items/ModItems.java @@ -1,8 +1,12 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.blocks.ModBlocks; +import com.ldtteam.structurize.api.util.constant.Constants; +import net.minecraft.world.item.Item; +import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; + import java.util.function.Supplier; /** @@ -12,26 +16,43 @@ public final class ModItems { private ModItems() { /* prevent construction */ } - public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(Constants.MOD_ID); + private static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(Constants.MOD_ID); + + public static DeferredRegister.Items getRegistry() + { + return ITEMS; + } /* * Items */ - public static final DeferredItem buildTool; - public static final DeferredItem shapeTool; - public static final DeferredItem scanTool; - public static final DeferredItem tagTool; - public static final DeferredItem caliper; - public static final DeferredItem blockTagSubstitution; + public static final DeferredHolder buildTool; + public static final DeferredHolder shapeTool; + public static final DeferredHolder scanTool; + public static final DeferredHolder tagTool; + public static final DeferredHolder caliper; + public static final DeferredHolder blockTagSubstitution; + + /** + * Utility method to register an item + * @param name the registry key for the item + * @param item a factory/constructor to produce the item on demand + * @param any item subclass + * @return the item entry saved to the registry + */ + public static DeferredItem register(String name, java.util.function.Function item) + { + return ITEMS.registerItem(name.toLowerCase(), item, () -> new Item.Properties()); + } static { - buildTool = ITEMS.register("sceptergold", ItemBuildTool::new); - shapeTool = ITEMS.register("shapetool", ItemShapeTool::new); - scanTool = ITEMS.register("sceptersteel", (Supplier) ItemScanTool::new); - tagTool = ITEMS.register("sceptertag", (Supplier) ItemTagTool::new); - caliper = ITEMS.register("caliper", ItemCaliper::new); - blockTagSubstitution = ITEMS.register("blockTagSubstitution".toLowerCase(), ItemTagSubstitution::new); + buildTool = register("sceptergold", ItemBuildTool::new); + shapeTool = register("shapetool", ItemShapeTool::new); + scanTool = register("sceptersteel", ItemScanTool::new); + tagTool = register("sceptertag", ItemTagTool::new); + caliper = register("caliper", ItemCaliper::new); + blockTagSubstitution = register("blockTagSubstitution", ItemTagSubstitution::new); } } diff --git a/src/main/java/com/ldtteam/structurize/management/Manager.java b/src/main/java/com/ldtteam/structurize/management/Manager.java index 60cbfebe8b..4f59af8b85 100644 --- a/src/main/java/com/ldtteam/structurize/management/Manager.java +++ b/src/main/java/com/ldtteam/structurize/management/Manager.java @@ -1,24 +1,28 @@ package com.ldtteam.structurize.management; -import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.Shape; +import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.operations.ITickedWorldOperation; import com.ldtteam.structurize.operations.RedoOperation; import com.ldtteam.structurize.operations.UndoOperation; import com.ldtteam.structurize.placement.StructurePlacementUtils; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.ChangeStorage; -import com.ldtteam.structurize.util.ITickedWorldOperation; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.Shape; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.SavedDataStorage; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + import java.util.*; import static com.ldtteam.structurize.operations.UndoOperation.UNDO_PREFIX; @@ -44,6 +48,11 @@ public final class Manager */ private static final LinkedList scanToolOperationPool = new LinkedList<>(); + /** + * Pseudo unique id for the server + */ + private static volatile UUID serverUUID = null; + private Manager() { //Hides default constructor. @@ -56,19 +65,15 @@ private Manager() */ public static void onWorldTick(final ServerLevel world) { - int count = 0; if (!scanToolOperationPool.isEmpty()) { - while (count++ <= Structurize.getConfig().getServer().maxOperationsPerTick.get()) + final ITickedWorldOperation operation = scanToolOperationPool.peek(); + if (operation != null && operation.apply(world)) { - final ITickedWorldOperation operation = scanToolOperationPool.peek(); - if (operation != null && operation.apply(world)) + scanToolOperationPool.pop(); + if (!(operation instanceof UndoOperation || operation instanceof RedoOperation)) { - scanToolOperationPool.pop(); - if (!(operation instanceof UndoOperation || operation instanceof RedoOperation)) - { - addToUndoRedoCache(operation.getChangeStorage()); - } + addToUndoRedoCache(operation.getChangeStorage()); } } } @@ -128,7 +133,8 @@ public static List getChangeStoragesForPlayer(final UUID player) * @param inputFillBlock the fill block. * @param hollow if hollow or not. * @param player the player. - * @param rotMir the mirror and the rotation. + * @param mirror the mirror. + * @param rotation the rotation. */ public static void pasteStructure( final ServerLevel server, @@ -143,11 +149,11 @@ public static void pasteStructure( final ItemStack inputFillBlock, final boolean hollow, final ServerPlayer player, - final RotationMirror rotMir, - final HolderLookup.Provider provider) + final Mirror mirror, + final Rotation rotation) { - final Blueprint blueprint = Manager.getStructureFromFormula(width, length, height, frequency, equation, shape, inputBlock, inputFillBlock, hollow, provider); - StructurePlacementUtils.loadAndPlaceStructureWithRotation(server, blueprint, pos, rotMir, true, player); + final Blueprint blueprint = Manager.getStructureFromFormula(width, length, height, frequency, equation, shape, inputBlock, inputFillBlock, hollow); + StructurePlacementUtils.loadAndPlaceStructureWithRotation(server, blueprint, pos, rotation, mirror, true, player); } /** @@ -172,9 +178,7 @@ public static Blueprint getStructureFromFormula( final String equation, final Shape shape, final ItemStack inputBlock, - final ItemStack inputFillBlock, - final boolean hollow, - final HolderLookup.Provider provider) + final ItemStack inputFillBlock, final boolean hollow) { final Blueprint blueprint; final BlockState mainBlock = BlockUtils.getBlockStateFromStack(inputBlock, Blocks.GOLD_BLOCK.defaultBlockState()); @@ -182,35 +186,35 @@ public static Blueprint getStructureFromFormula( if (shape == Shape.SPHERE || shape == Shape.HALF_SPHERE || shape == Shape.BOWL) { - blueprint = generateSphere(height / 2, mainBlock, fillBlock, hollow, shape, provider); + blueprint = generateSphere(height / 2, mainBlock, fillBlock, hollow, shape); } else if (shape == Shape.CUBE) { - blueprint = generateCube(height, width, length, mainBlock, fillBlock, hollow, provider); + blueprint = generateCube(height, width, length, mainBlock, fillBlock, hollow); } else if (shape == Shape.WAVE) { - blueprint = generateWave(height, width, length, frequency, mainBlock, true, provider); + blueprint = generateWave(height, width, length, frequency, mainBlock, true); } else if (shape == Shape.WAVE_3D) { - blueprint = generateWave(height, width, length, frequency, mainBlock, false, provider); + blueprint = generateWave(height, width, length, frequency, mainBlock, false); } else if (shape == Shape.CYLINDER) { - blueprint = generateCylinder(height, width, mainBlock, fillBlock, hollow, provider); + blueprint = generateCylinder(height, width, mainBlock, fillBlock, hollow); } else if (shape == Shape.PYRAMID || shape == Shape.UPSIDE_DOWN_PYRAMID || shape == Shape.DIAMOND) { - blueprint = generatePyramid(height, mainBlock, fillBlock, hollow, shape, provider); + blueprint = generatePyramid(height, mainBlock, fillBlock, hollow, shape); } else if (shape == Shape.CONE) { - blueprint = generateCone(height, width, mainBlock, fillBlock, hollow, shape, provider); + blueprint = generateCone(height, width, mainBlock, fillBlock, hollow, shape); } else { - blueprint = generateRandomShape(height, width, length, equation, mainBlock, provider); + blueprint = generateRandomShape(height, width, length, equation, mainBlock); } return blueprint; } @@ -220,8 +224,7 @@ private static Blueprint generatePyramid( final BlockState block, final BlockState fillBlock, final boolean hollow, - final Shape shape, - final HolderLookup.Provider provider) + final Shape shape) { final int height = shape == Shape.DIAMOND ? inputHeight : inputHeight * 2; final int hHeight = height / 2; @@ -256,7 +259,7 @@ private static Blueprint generatePyramid( } } - final Blueprint blueprint = new Blueprint((short) height, (short) (shape == Shape.DIAMOND ? height : inputHeight + 2), (short) height, provider); + final Blueprint blueprint = new Blueprint((short) height, (short) (shape == Shape.DIAMOND ? height : inputHeight + 2), (short) height); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -267,8 +270,7 @@ private static Blueprint generateCone( final BlockState block, final BlockState fillBlock, final boolean hollow, - final Shape shape, - final HolderLookup.Provider provider) + final Shape shape) { final int height = shape == Shape.DIAMOND ? inputHeight : inputHeight * 2; final Map posList = new HashMap<>(); @@ -293,7 +295,7 @@ private static Blueprint generateCone( } } - final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2), provider); + final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -314,8 +316,7 @@ private static Blueprint generateCube( final int length, final BlockState block, final BlockState fillBlock, - final boolean hollow, - final HolderLookup.Provider provider) + final boolean hollow) { final Map posList = new HashMap<>(); for (int y = 0; y < height; y++) @@ -335,7 +336,7 @@ else if (!hollow) } } } - final Blueprint blueprint = new Blueprint((short) width, (short) height, (short) length, provider); + final Blueprint blueprint = new Blueprint((short) width, (short) height, (short) length); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -354,8 +355,7 @@ private static Blueprint generateSphere( final BlockState block, final BlockState fillBlock, final boolean hollow, - final Shape shape, - final HolderLookup.Provider provider) + final Shape shape) { final Map posList = new HashMap<>(); for (int y = 0; y <= height + 1; y++) @@ -387,7 +387,7 @@ private static Blueprint generateSphere( } } - final Blueprint blueprint = new Blueprint((short) ((height + 2) * 2), (short) ((height + 2) * 2), (short) ((height + 2) * 2), provider); + final Blueprint blueprint = new Blueprint((short) ((height + 2) * 2), (short) ((height + 2) * 2), (short) ((height + 2) * 2)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -406,8 +406,7 @@ private static Blueprint generateCylinder( final int width, final BlockState block, final BlockState fillBlock, - final boolean hollow, - final HolderLookup.Provider provider) + final boolean hollow) { final Map posList = new HashMap<>(); for (int x = 0; x < width; x++) @@ -430,7 +429,7 @@ private static Blueprint generateCylinder( } } - final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2), provider); + final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -449,8 +448,7 @@ private static Blueprint generateWave( final int length, final int frequency, final BlockState block, - final boolean flat, - final HolderLookup.Provider provider) + final boolean flat) { final Map posList = new HashMap<>(); for (int x = 0; x < length; x++) @@ -468,7 +466,7 @@ private static Blueprint generateWave( } } - final Blueprint blueprint = new Blueprint((short) length, (short) (frequency * 2 + 1 + (!flat ? width * 2 : 0)), (short) (width * 2 + 1), provider); + final Blueprint blueprint = new Blueprint((short) length, (short) (frequency * 2 + 1 + (!flat ? width * 2 : 0)), (short) (width * 2 + 1)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -483,7 +481,7 @@ private static Blueprint generateWave( * @param block the block. * @return the created blueprint */ - public static Blueprint generateRandomShape(final int height, final int width, final int length, final String equation, final BlockState block, final HolderLookup.Provider provider) + public static Blueprint generateRandomShape(final int height, final int width, final int length, final String equation, final BlockState block) { /*Expression e = new Expression(equation); final Argument argumentX = new Argument("x = 0"); @@ -544,7 +542,7 @@ public static void undo(final Player player, final int operationID) final List list = changeQueue.get(player.getUUID()); if (list == null || list.isEmpty()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.notfound")); return; } @@ -555,11 +553,11 @@ public static void undo(final Player player, final int operationID) { if (!storage.isDone()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.inprogress", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.inprogress", storage.getOperation())); return; } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.add", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.add", storage.getOperation())); addToQueue(new UndoOperation(player, storage)); if (storage.getOperation().toString().indexOf(UNDO_PREFIX) == 0) { @@ -569,7 +567,7 @@ public static void undo(final Player player, final int operationID) } } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.notfound")); } /** @@ -583,7 +581,7 @@ public static void redo(final Player player, final int operationID) final List list = changeQueue.get(player.getUUID()); if (list == null || list.isEmpty()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.notfound")); return; } @@ -593,17 +591,64 @@ public static void redo(final Player player, final int operationID) { if (!storage.isDone()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.inprogress", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.inprogress", storage.getOperation())); return; } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.add", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.add", storage.getOperation())); addToQueue(new RedoOperation(player, storage)); return; } } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.notfound")); + } + + /** + * Get the Universal Unique ID for the server. + * + * @return the server Universal Unique ID for ther + */ + public static UUID getServerUUID() + { + if (serverUUID == null) + { + return generateOrRetrieveUUID(); + } + return serverUUID; + } + + /** + * Generate or retrieve the UUID of the server. + * + * @return the UUID. + */ + private static UUID generateOrRetrieveUUID() + { + final SavedDataStorage storage = ServerLifecycleHooks.getCurrentServer().overworld().getDataStorage(); + final UUIDStorage loaded = storage.computeIfAbsent(UUIDStorage.TYPE); + if (loaded.getUUID() != null) + { + Manager.setServerUUID(loaded.getUUID()); + return serverUUID; + } + + final UUIDStorage created = new UUIDStorage(UUID.randomUUID()); + storage.set(UUIDStorage.TYPE, created); + Manager.setServerUUID(created.getUUID()); + Log.getLogger().info(String.format("New Server UUID %s", serverUUID)); + + return serverUUID; + } + + /** + * Set the server UUID. + * + * @param uuid the universal unique id + */ + public static void setServerUUID(final UUID uuid) + { + serverUUID = uuid; } /** diff --git a/src/main/java/com/ldtteam/structurize/management/UUIDStorage.java b/src/main/java/com/ldtteam/structurize/management/UUIDStorage.java new file mode 100644 index 0000000000..999a13a589 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/management/UUIDStorage.java @@ -0,0 +1,39 @@ +package com.ldtteam.structurize.management; + +import com.mojang.serialization.Codec; +import net.minecraft.core.UUIDUtil; +import net.minecraft.resources.Identifier; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; + +import java.util.UUID; + +/** + * Server-scoped identifier used to keep operation history stable across restarts. + */ +public final class UUIDStorage extends SavedData +{ + public static final Codec CODEC = UUIDUtil.CODEC.xmap(UUIDStorage::new, UUIDStorage::getUUID); + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.fromNamespaceAndPath("structurize", "server_uuid"), + level -> new UUIDStorage(), + level -> CODEC + ); + + private final UUID uuid; + + public UUIDStorage() + { + this(UUID.randomUUID()); + } + + public UUIDStorage(final UUID uuid) + { + this.uuid = uuid; + } + + public UUID getUUID() + { + return uuid; + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/NetworkChannel.java b/src/main/java/com/ldtteam/structurize/network/NetworkChannel.java new file mode 100644 index 0000000000..8991082096 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/NetworkChannel.java @@ -0,0 +1,311 @@ +package com.ldtteam.structurize.network; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Maps; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.network.messages.*; +import com.ldtteam.structurize.network.messages.splitting.SplitPacketMessage; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.LevelChunk; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.client.network.ClientPacketDistributor; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import net.neoforged.neoforge.network.registration.PayloadRegistrar; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Compatibility adapter between Structurize's indexed messages and NeoForge payloads. + */ +public class NetworkChannel +{ + private final String channelName; + private final Map> messagesTypes = Maps.newHashMap(); + private final Map, Integer> messageTypeToIdMap = Maps.newHashMap(); + private final Cache> messageCache = CacheBuilder.newBuilder() + .expireAfterAccess(1, TimeUnit.MINUTES) + .concurrencyLevel(8) + .build(); + private final AtomicInteger messageCounter = new AtomicInteger(); + + public NetworkChannel(final String channelName) + { + this.channelName = channelName; + } + + public void registerCommonMessages(final PayloadRegistrar registrar) + { + messagesTypes.put(0, new NetworkingMessageEntry<>(SplitPacketMessage::new, SplitPacketMessage.class)); + + int idx = 0; + registerMessage(++idx, RemoveBlockMessage.class, RemoveBlockMessage::new); + registerMessage(++idx, RemoveEntityMessage.class, RemoveEntityMessage::new); + registerMessage(++idx, SaveScanMessage.class, SaveScanMessage::new); + registerMessage(++idx, ReplaceBlockMessage.class, ReplaceBlockMessage::new); + registerMessage(++idx, FillTopPlaceholderMessage.class, FillTopPlaceholderMessage::new); + registerMessage(++idx, ScanOnServerMessage.class, ScanOnServerMessage::new); + registerMessage(++idx, ServerUUIDMessage.class, ServerUUIDMessage::new); + registerMessage(++idx, UndoRedoMessage.class, UndoRedoMessage::new); + registerMessage(++idx, UpdateScanToolMessage.class, UpdateScanToolMessage::new); + registerMessage(++idx, UpdateClientRender.class, UpdateClientRender::new); + registerMessage(++idx, BuildToolPlacementMessage.class, BuildToolPlacementMessage::new); + registerMessage(++idx, ShowScanMessage.class, ShowScanMessage::new); + + registerMessage(++idx, AddRemoveTagMessage.class, AddRemoveTagMessage::new); + registerMessage(++idx, SetTagInTool.class, SetTagInTool::new); + registerMessage(++idx, OperationHistoryMessage.class, OperationHistoryMessage::new); + + registerMessage(++idx, NotifyServerAboutStructurePacksMessage.class, NotifyServerAboutStructurePacksMessage::new); + // Preserve the historical second index for this class; dispatch uses the exact registered id. + registerMessage(++idx, BuildToolPlacementMessage.class, BuildToolPlacementMessage::new); + registerMessage(++idx, BlueprintSyncMessage.class, BlueprintSyncMessage::new); + registerMessage(++idx, SyncSettingsToServer.class, SyncSettingsToServer::new); + registerMessage(++idx, SyncPreviewCacheToServer.class, SyncPreviewCacheToServer::new); + + registerMessage(++idx, NotifyClientAboutStructurePacksMessage.class, NotifyClientAboutStructurePacksMessage::new); + registerMessage(++idx, TransferStructurePackToClient.class, TransferStructurePackToClient::new); + registerMessage(++idx, ClientBlueprintRequestMessage.class, ClientBlueprintRequestMessage::new); + registerMessage(++idx, SyncPreviewCacheToClient.class, SyncPreviewCacheToClient::new); + + registerMessage(++idx, ItemMiddleMouseMessage.class, ItemMiddleMouseMessage::new); + registerMessage(++idx, ScanToolTeleportMessage.class, ScanToolTeleportMessage::new); + registerMessage(++idx, AbsorbBlockMessage.class, AbsorbBlockMessage::new); + + for (final Map.Entry> entry : messagesTypes.entrySet()) + { + final IPayloadHandler handler = + (payload, context) -> handleMessage(payload.messageId(), payload.data(), context); + registrar.playBidirectional( + WrappedMessage.typeFor(entry.getKey()), + WrappedMessage.CODEC, + handler, + handler); + } + } + + private void registerMessage(final int id, + final Class msgClazz, + final Function msgCreator) + { + messagesTypes.put(id, new NetworkingMessageEntry<>(msgCreator, msgClazz)); + messageTypeToIdMap.put(msgClazz, id); + } + + public void sendToServer(final IMessage msg) + { + handleSplitting(msg, ClientPacketDistributor::sendToServer); + } + + public void sendToPlayer(final IMessage msg, final ServerPlayer player) + { + handleSplitting(msg, payload -> PacketDistributor.sendToPlayer(player, payload)); + } + + public void sendToOrigin(final IMessage msg, final NetworkContext context) + { + final ServerPlayer player = context.getSender(); + if (player != null) + { + sendToPlayer(msg, player); + } + else + { + sendToServer(msg); + } + } + + public void sendToDimension(final IMessage msg, final ServerLevel dimension) + { + PacketDistributor.sendToPlayersInDimension(dimension, wrapForTransport(msg)); + } + + public void sendToPosition(final IMessage msg, + final ServerLevel level, + final ServerPlayer excludedPlayer, + final double x, + final double y, + final double z, + final double radius) + { + PacketDistributor.sendToPlayersNear(level, excludedPlayer, x, y, z, radius, wrapForTransport(msg)); + } + + public void sendToEveryone(final IMessage msg) + { + PacketDistributor.sendToAllPlayers(wrapForTransport(msg)); + } + + public void sendToTrackingEntity(final IMessage msg, final Entity entity) + { + PacketDistributor.sendToPlayersTrackingEntity(entity, wrapForTransport(msg)); + } + + public void sendToTrackingEntityAndSelf(final IMessage msg, final Entity entity) + { + PacketDistributor.sendToPlayersTrackingEntityAndSelf(entity, wrapForTransport(msg)); + } + + public void sendToTrackingChunk(final IMessage msg, final LevelChunk chunk) + { + if (!(chunk.getLevel() instanceof final ServerLevel level)) + { + throw new IllegalArgumentException("Cannot track a client chunk: " + chunk.getLevel().getClass().getName()); + } + PacketDistributor.sendToPlayersTrackingChunk(level, chunk.getPos(), wrapForTransport(msg)); + } + + private CustomPacketPayload wrapForTransport(final IMessage msg) + { + final int messageId = messageTypeToIdMap.getOrDefault(msg.getClass(), -1); + if (messageId == -1) + { + throw new IllegalArgumentException("The message is unknown to this channel!"); + } + return new WrappedMessage(messageId, serialize(msg)); + } + + private byte[] serialize(final IMessage msg) + { + final ByteBuf buffer = Unpooled.buffer(); + try + { + msg.toBytes(new FriendlyByteBuf(buffer)); + return Arrays.copyOf(buffer.array(), buffer.readableBytes()); + } + finally + { + buffer.release(); + } + } + + private void handleSplitting(final IMessage msg, final Consumer sender) + { + final int messageId = messageTypeToIdMap.getOrDefault(msg.getClass(), -1); + if (messageId == -1) + { + throw new IllegalArgumentException("The message is unknown to this channel!"); + } + + final byte[] data = serialize(msg); + final int maxPacketSize = msg.getExecutionSide() == LogicalSide.SERVER ? 30000 : 943718; + int currentIndex = 0; + int packetIndex = 0; + final int communicationId = messageCounter.getAndIncrement(); + + while (currentIndex < data.length) + { + messagesTypes.get(messageId).onSplitting(packetIndex); + final int length = Math.min(maxPacketSize, data.length - currentIndex); + final byte[] packetData = Arrays.copyOfRange(data, currentIndex, currentIndex + length); + sender.accept(new WrappedMessage( + 0, + serialize(new SplitPacketMessage( + communicationId, + packetIndex++, + currentIndex + length >= data.length, + messageId, + packetData)))); + currentIndex += length; + } + } + + private void handleMessage(final int messageId, final byte[] data, final IPayloadContext neoContext) + { + final NetworkContext context = new NetworkContext(neoContext); + final FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.wrappedBuffer(data)); + final IMessage message; + try + { + if (messageId == 0) + { + message = new SplitPacketMessage(buffer); + } + else + { + final NetworkingMessageEntry entry = messagesTypes.get(messageId); + if (entry == null) + { + throw new IllegalArgumentException("Unknown Structurize message id: " + messageId); + } + message = entry.getCreator().apply(buffer); + } + } + finally + { + buffer.release(); + } + + final LogicalSide receivingSide = neoContext.flow().getReceptionSide(); + if (message.getExecutionSide() != null && receivingSide != message.getExecutionSide()) + { + Log.getLogger().warn("Receiving {} at wrong side!", message.getClass().getName()); + return; + } + + neoContext.enqueueWork(() -> message.onExecute(context, receivingSide == LogicalSide.SERVER)); + } + + public Cache> getMessageCache() + { + return messageCache; + } + + public Map> getMessagesTypes() + { + return messagesTypes; + } + + public String getChannelName() + { + return channelName; + } + + public static final class NetworkingMessageEntry + { + private final AtomicBoolean hasWarned = new AtomicBoolean(true); + private final Function creator; + private final Class clazz; + + private NetworkingMessageEntry(final Function creator, final Class clazz) + { + this.creator = creator; + this.clazz = clazz; + } + + public Function getCreator() + { + return creator; + } + + public void onSplitting(final int packetIndex) + { + if (packetIndex != 1) + { + return; + } + + if (hasWarned.getAndSet(false)) + { + Log.getLogger() + .warn("Splitting message: {} it is too big to send normally. This message is only printed once", clazz.getName()); + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/NetworkContext.java b/src/main/java/com/ldtteam/structurize/network/NetworkContext.java new file mode 100644 index 0000000000..3665e1144b --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/NetworkContext.java @@ -0,0 +1,40 @@ +package com.ldtteam.structurize.network; + +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.Nullable; + +/** + * Compatibility facade over the NeoForge payload context. It preserves the + * server-player lookup that the legacy message handlers relied on. + */ +public final class NetworkContext +{ + private final IPayloadContext payloadContext; + + public NetworkContext(final IPayloadContext payloadContext) + { + this.payloadContext = payloadContext; + } + + public IPayloadContext payload() + { + return payloadContext; + } + + @Nullable + public ServerPlayer getSender() + { + return payloadContext.player() instanceof final ServerPlayer player ? player : null; + } + + public boolean isClientOrigin() + { + return payloadContext.flow().isServerbound(); + } + + public void enqueueWork(final Runnable work) + { + payloadContext.enqueueWork(work); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/WrappedMessage.java b/src/main/java/com/ldtteam/structurize/network/WrappedMessage.java new file mode 100644 index 0000000000..dc4f54dd17 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/WrappedMessage.java @@ -0,0 +1,46 @@ +package com.ldtteam.structurize.network; + +import com.ldtteam.structurize.api.util.constant.Constants; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Transport frame used to migrate Structurize's indexed messages onto + * NeoForge payloads without changing each message's buffer format. + */ +public record WrappedMessage(int messageId, byte[] data) implements CustomPacketPayload +{ + private static final Map> TYPES = new ConcurrentHashMap<>(); + public static final StreamCodec CODEC = CustomPacketPayload.codec( + WrappedMessage::write, + WrappedMessage::read); + + public static Type typeFor(final int messageId) + { + return TYPES.computeIfAbsent(messageId, id -> new Type<>( + Identifier.fromNamespaceAndPath(Constants.MOD_ID, "message/" + id))); + } + + private static WrappedMessage read(final FriendlyByteBuf buf) + { + return new WrappedMessage(buf.readVarInt(), buf.readByteArray()); + } + + private void write(final FriendlyByteBuf buf) + { + buf.writeVarInt(messageId); + buf.writeByteArray(data); + } + + @Override + public Type type() + { + return typeFor(messageId); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java index bb250709b6..130fbacddb 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java @@ -1,22 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.items.ItemTagSubstitution; +import com.ldtteam.structurize.items.ItemTagSubstitution; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Sent client to server to request that the currently held item should "absorb" a new replacement block. */ -public class AbsorbBlockMessage extends AbstractServerPlayMessage +public class AbsorbBlockMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "absorb_block", AbsorbBlockMessage::new); private final BlockPos pos; private final ItemStack stack; @@ -27,7 +26,6 @@ public class AbsorbBlockMessage extends AbstractServerPlayMessage */ public AbsorbBlockMessage(@NotNull final BlockPos pos, @NotNull final ItemStack stack) { - super(TYPE); this.pos = pos; this.stack = stack; } @@ -36,11 +34,10 @@ public AbsorbBlockMessage(@NotNull final BlockPos pos, @NotNull final ItemStack * Deserialize * @param buf the network buffer */ - protected AbsorbBlockMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public AbsorbBlockMessage(@NotNull final FriendlyByteBuf buf) { - super(buf, type); this.pos = buf.readBlockPos(); - this.stack = ItemStack.STREAM_CODEC.decode(buf); + this.stack = ItemStackNbtHelper.readNetworkStack(buf); } /** @@ -48,16 +45,24 @@ protected AbsorbBlockMessage(@NotNull final RegistryFriendlyByteBuf buf, final P * @param buf network data byte buffer */ @Override - protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) + public void toBytes(@NotNull final FriendlyByteBuf buf) { buf.writeBlockPos(this.pos); - ItemStack.STREAM_CODEC.encode(buf, this.stack); + ItemStackNbtHelper.writeNetworkStack(buf, this.stack); } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public LogicalSide getExecutionSide() { - final ItemStack current = player.getInventory().getSelected(); + return LogicalSide.SERVER; + } + + @Override + public void onExecute(@NotNull final NetworkContext ctxIn, final boolean isLogicalServer) + { + final ServerPlayer player = ctxIn.getSender(); + final ItemStack current = player.getInventory().getSelectedItem(); if (current.getItem() instanceof ItemTagSubstitution anchor) { diff --git a/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java index a75752ca24..95ef45bb2c 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java @@ -1,32 +1,28 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Messages for adding or removing a tag */ -public class AddRemoveTagMessage extends AbstractServerPlayMessage +public class AddRemoveTagMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "add_remove_tag", AddRemoveTagMessage::new); - /** * Whether we add or remove a tag */ - private final boolean add; + private boolean add = false; /** * The tag to use */ - private final String tag; + private String tag = ""; /** * THe te's position @@ -41,9 +37,8 @@ public class AddRemoveTagMessage extends AbstractServerPlayMessage /** * Empty constructor used when registering the */ - protected AddRemoveTagMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public AddRemoveTagMessage(final FriendlyByteBuf buf) { - super(buf, type); this.add = buf.readBoolean(); this.tag = buf.readUtf(32767); this.anchorPos = buf.readBlockPos(); @@ -52,7 +47,6 @@ protected AddRemoveTagMessage(final RegistryFriendlyByteBuf buf, final PlayMessa public AddRemoveTagMessage(final boolean add, final String tag, final BlockPos tagPos, final BlockPos anchorPos) { - super(TYPE); this.anchorPos = anchorPos; this.tagPos = tagPos; this.add = add; @@ -60,7 +54,7 @@ public AddRemoveTagMessage(final boolean add, final String tag, final BlockPos t } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBoolean(add); buf.writeUtf(tag); @@ -68,11 +62,22 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeBlockPos(tagPos); } + @Nullable @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final BlockEntity te = player.level().getBlockEntity(anchorPos); + if (ctxIn.getSender() == null) + { + return; + } + + final BlockEntity te = ctxIn.getSender().level().getBlockEntity(anchorPos); if (te instanceof IBlueprintDataProviderBE) { final IBlueprintDataProviderBE dataTE = (IBlueprintDataProviderBE) te; diff --git a/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java index 840e329369..e1ca92a136 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java @@ -1,24 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.BlueprintPlacementHandling; -import com.ldtteam.structurize.api.RotationMirror; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.apache.commons.io.FilenameUtils; +import org.jetbrains.annotations.Nullable; /** * Sends a blueprint from the client to the server. */ -public class BlueprintSyncMessage extends AbstractServerPlayMessage +public class BlueprintSyncMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "blueprint_sync", BlueprintSyncMessage::new); - /** * Structure placement info. */ @@ -27,26 +24,27 @@ public class BlueprintSyncMessage extends AbstractServerPlayMessage public String structurePackId; public final String blueprintPath; public final BlockPos pos; - public final RotationMirror rotationMirror; + public final Rotation rotation; + public final Mirror mirror; /** * Blueprint data future. */ - public final byte[] blueprintData; + public byte[] blueprintData; /** * Buffer reading message constructor. */ - protected BlueprintSyncMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public BlueprintSyncMessage(final FriendlyByteBuf buf) { - super(buf, type); this.type = BuildToolPlacementMessage.HandlerType.values()[buf.readInt()]; this.handlerId = buf.readUtf(32767); this.structurePackId = buf.readUtf(32767); this.blueprintPath = FilenameUtils.normalize(buf.readUtf(32767)); this.pos = buf.readBlockPos(); - this.rotationMirror = RotationMirror.values()[buf.readInt()]; + this.rotation = Rotation.values()[buf.readInt()]; + this.mirror = Mirror.values()[buf.readInt()]; this.blueprintData = buf.readByteArray(); } @@ -61,19 +59,19 @@ public BlueprintSyncMessage( final ClientBlueprintRequestMessage msg, final byte[] blueprintData) { - super(TYPE); this.type = msg.type; this.handlerId = msg.handlerId; this.structurePackId = msg.structurePackId; this.blueprintPath = msg.blueprintPath; this.pos = msg.pos; - this.rotationMirror = msg.rotationMirror; + this.rotation = msg.rotation; + this.mirror = msg.mirror; this.blueprintData = blueprintData; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(this.type.ordinal()); buf.writeUtf(this.handlerId); @@ -81,17 +79,25 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeUtf(this.structurePackId); buf.writeUtf(this.blueprintPath); buf.writeBlockPos(this.pos); - buf.writeInt(this.rotationMirror.ordinal()); + buf.writeInt(this.rotation.ordinal()); + buf.writeInt(this.mirror.ordinal()); buf.writeByteArray(this.blueprintData); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { if (Structurize.getConfig().getServer().allowPlayerSchematics.get()) { - BlueprintPlacementHandling.handlePlacement(this, player); + BlueprintPlacementHandling.handlePlacement(this, ctxIn.getSender()); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java index 7478afba58..86a1fd5b94 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java @@ -1,25 +1,23 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.BlueprintPlacementHandling; -import com.ldtteam.structurize.api.RotationMirror; import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Send build tool data to the server. Verify the data on the server side and then place the blueprint. * This also buffers the incoming messages, places one per tick and loads the file off-thread. */ -public class BuildToolPlacementMessage extends AbstractServerPlayMessage +public class BuildToolPlacementMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "buildtool_placement", BuildToolPlacementMessage::new); - /** * Identify the Client side handler. */ @@ -32,7 +30,8 @@ public class BuildToolPlacementMessage extends AbstractServerPlayMessage public final String structurePackId; public final String blueprintPath; public final BlockPos pos; - public final RotationMirror rotationMirror; + public final Rotation rotation; + public final Mirror mirror; /** * Cached placement info. @@ -54,16 +53,16 @@ public enum HandlerType /** * Buffer reading message constructor. */ - protected BuildToolPlacementMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public BuildToolPlacementMessage(final FriendlyByteBuf buf) { - super(buf, type); this.type = HandlerType.values()[buf.readInt()]; this.handlerId = buf.readUtf(32767); this.structurePackId = buf.readUtf(32767); this.blueprintPath = buf.readUtf(32767); this.pos = buf.readBlockPos(); - this.rotationMirror = RotationMirror.values()[buf.readInt()]; + this.rotation = Rotation.values()[buf.readInt()]; + this.mirror = Mirror.values()[buf.readInt()]; } /** @@ -74,7 +73,8 @@ protected BuildToolPlacementMessage(final RegistryFriendlyByteBuf buf, final Pla * @param structurePackId the id of the pack. * @param blueprintPath the path of the structure in the pack. * @param pos the position of the blueprint. - * @param rotMir the rotation and the mirror of the blueprint. + * @param rotation the rotation of the blueprint. + * @param mirror the mirror of the blueprint. */ public BuildToolPlacementMessage( final HandlerType type, @@ -82,16 +82,17 @@ public BuildToolPlacementMessage( final String structurePackId, final String blueprintPath, final BlockPos pos, - final RotationMirror rotMir) + final Rotation rotation, + final Mirror mirror) { - super(TYPE); this.type = type; this.handlerId = handlerId; this.structurePackId = structurePackId; this.blueprintPath = blueprintPath; this.pos = pos; - this.rotationMirror = rotMir; + this.rotation = rotation; + this.mirror = mirror; } /** @@ -100,14 +101,14 @@ public BuildToolPlacementMessage( */ public BuildToolPlacementMessage(final BlueprintSyncMessage msg, final ServerPlayer player, final Level world) { - super(TYPE); this.type = msg.type; this.handlerId = msg.handlerId; this.structurePackId = msg.structurePackId; this.blueprintPath = msg.blueprintPath; this.pos = msg.pos; - this.rotationMirror = msg.rotationMirror; + this.rotation = msg.rotation; + this.mirror = msg.mirror; this.clientPack = true; this.player = player; @@ -120,7 +121,7 @@ public BuildToolPlacementMessage(final BlueprintSyncMessage msg, final ServerPla * @param buf The buffer being written to. */ @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(this.type.ordinal()); buf.writeUtf(this.handlerId); @@ -128,14 +129,22 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeUtf(this.structurePackId); buf.writeUtf(this.blueprintPath); buf.writeBlockPos(this.pos); - buf.writeInt(this.rotationMirror.ordinal()); + buf.writeInt(this.rotation.ordinal()); + buf.writeInt(this.mirror.ordinal()); } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public LogicalSide getExecutionSide() { - world = player.level(); - this.player = player; + return LogicalSide.SERVER; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + world = ctxIn.getSender().level(); + player = ctxIn.getSender(); BlueprintPlacementHandling.handlePlacement(this); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java index 07b4fc0a38..44d83872f6 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java @@ -1,23 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.storage.ClientFutureProcessor; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Request a blueprint from the client. */ -public class ClientBlueprintRequestMessage extends AbstractClientPlayMessage +public class ClientBlueprintRequestMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "blueprint_request", ClientBlueprintRequestMessage::new); - /** * Structure placement info. */ @@ -26,22 +24,23 @@ public class ClientBlueprintRequestMessage extends AbstractClientPlayMessage public final String structurePackId; public final String blueprintPath; public final BlockPos pos; - public final RotationMirror rotationMirror; + public final Rotation rotation; + public final Mirror mirror; /** * Buffer reading message constructor. */ - protected ClientBlueprintRequestMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ClientBlueprintRequestMessage(final FriendlyByteBuf buf) { - super(buf, type); this.type = BuildToolPlacementMessage.HandlerType.values()[buf.readInt()]; this.handlerId = buf.readUtf(32767); this.structurePackId = buf.readUtf(32767); this.blueprintPath = buf.readUtf(32767); this.pos = buf.readBlockPos(); - this.rotationMirror = RotationMirror.values()[buf.readInt()]; + this.rotation = Rotation.values()[buf.readInt()]; + this.mirror = Mirror.values()[buf.readInt()]; } /** @@ -51,18 +50,18 @@ protected ClientBlueprintRequestMessage(final RegistryFriendlyByteBuf buf, final */ public ClientBlueprintRequestMessage(final BuildToolPlacementMessage msg) { - super(TYPE); this.type = msg.type; this.handlerId = msg.handlerId; this.structurePackId = msg.structurePackId; this.blueprintPath = msg.blueprintPath; this.pos = msg.pos; - this.rotationMirror = msg.rotationMirror; + this.rotation = msg.rotation; + this.mirror = msg.mirror; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(this.type.ordinal()); buf.writeUtf(this.handlerId); @@ -70,16 +69,24 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeUtf(this.structurePackId); buf.writeUtf(this.blueprintPath); buf.writeBlockPos(this.pos); - buf.writeInt(this.rotationMirror.ordinal()); + buf.writeInt(this.rotation.ordinal()); + buf.writeInt(this.mirror.ordinal()); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; } @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { ClientFutureProcessor.queueBlueprintData(new ClientFutureProcessor.BlueprintDataProcessingData(StructurePacks.getBlueprintDataFuture(structurePackId, blueprintPath), (blueprintData) -> { if (blueprintData != null) { - new BlueprintSyncMessage(this, blueprintData).sendToServer(); + Network.getNetwork().sendToServer(new BlueprintSyncMessage(this, blueprintData)); } })); } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java index f4a0236e04..63431d22ea 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java @@ -1,22 +1,18 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.util.PlacerholderFillOperation; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message to replace a block from the world with another one. */ -public class FillTopPlaceholderMessage extends AbstractServerPlayMessage +public class FillTopPlaceholderMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "fill_top_placeholder", FillTopPlaceholderMessage::new); - /** * Position to scan from. */ @@ -30,17 +26,16 @@ public class FillTopPlaceholderMessage extends AbstractServerPlayMessage /** * Fill parameters */ - private final double yStretch; - private final double circleRadiusMult; - private final int heightOffset; - private final int minDistToBlocks; + private double yStretch; + private double circleRadiusMult; + private int heightOffset; + private int minDistToBlocks; /** * Empty constructor used when registering the message. */ - protected FillTopPlaceholderMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public FillTopPlaceholderMessage(final FriendlyByteBuf buf) { - super(buf, type); this.from = buf.readBlockPos(); this.to = buf.readBlockPos(); this.yStretch = buf.readDouble(); @@ -54,8 +49,10 @@ protected FillTopPlaceholderMessage(final RegistryFriendlyByteBuf buf, final Pla * * @param pos1 start coordinate. * @param pos2 end coordinate. - * @param blockFrom the block to replace. - * @param blockTo the block to replace it with. + * @param yStretch vertical stretch factor. + * @param circleRadiusMult circle radius multiplier. + * @param heightOffset height offset. + * @param minDistToBlocks minimum distance from blocks. */ public FillTopPlaceholderMessage( final BlockPos pos1, @@ -65,7 +62,6 @@ public FillTopPlaceholderMessage( final int heightOffset, final int minDistToBlocks) { - super(TYPE); this.from = pos1; this.to = pos2; this.yStretch = yStretch; @@ -75,7 +71,7 @@ public FillTopPlaceholderMessage( } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBlockPos(from); buf.writeBlockPos(to); @@ -85,14 +81,21 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeInt(minDistToBlocks); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } - Manager.addToQueue(new PlacerholderFillOperation(from, to, player, yStretch, circleRadiusMult, heightOffset, minDistToBlocks)); + Manager.addToQueue(new PlacerholderFillOperation(from, to, ctxIn.getSender(), yStretch, circleRadiusMult, heightOffset, minDistToBlocks)); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/IMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/IMessage.java new file mode 100644 index 0000000000..e8695ddab6 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/messages/IMessage.java @@ -0,0 +1,36 @@ +package com.ldtteam.structurize.network.messages; + +import com.ldtteam.structurize.network.NetworkContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.Nullable; + +/** + * Interface for all network messages + */ +public interface IMessage +{ + /** + * Writes message data to buffer. + * + * @param buf network data byte buffer + */ + void toBytes(final FriendlyByteBuf buf); + + /** + * Which sides is message able to be executed at. + * + * @return CLIENT or SERVER or null (for both) + */ + @Nullable + LogicalSide getExecutionSide(); + + /** + * Executes message action. + * + * @param ctxIn network context of incoming message + * @param isLogicalServer whether message arrived at logical server side + */ + void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer); +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java index d8848cd2a7..b51c35ce1c 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java @@ -1,28 +1,23 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.IScrollableItem; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.IScrollableItem; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Notify server that client clicked or scrolled the middle mouse on a special item */ -public class ItemMiddleMouseMessage extends AbstractServerPlayMessage +public class ItemMiddleMouseMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "item_middle_mouse", ItemMiddleMouseMessage::new); - @Nullable private final BlockPos pos; - private final double deltaX; - private final double deltaY; + private final double delta; private final boolean ctrlKey; /** @@ -32,25 +27,20 @@ public class ItemMiddleMouseMessage extends AbstractServerPlayMessage */ public ItemMiddleMouseMessage(@Nullable final BlockPos pos, final boolean ctrlKey) { - super(TYPE); this.pos = pos; - this.deltaX = 0; - this.deltaY = 0; + this.delta = 0; this.ctrlKey = ctrlKey; } /** * Construct message for a middle mouse shift-scroll event. - * @param deltaX the scroll delta; negative is upwards - * @param deltaY the scroll delta; negative is upwards + * @param delta the scroll delta; negative is upwards * @param ctrlKey ctrl key is held */ - public ItemMiddleMouseMessage(final double deltaX, final double deltaY, final boolean ctrlKey) + public ItemMiddleMouseMessage(final double delta, final boolean ctrlKey) { - super(TYPE); this.pos = null; - this.deltaX = deltaX; - this.deltaY = deltaY; + this.delta = delta; this.ctrlKey = ctrlKey; } @@ -58,17 +48,15 @@ public ItemMiddleMouseMessage(final double deltaX, final double deltaY, final bo * Construct from network. * @param buf buffer */ - protected ItemMiddleMouseMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ItemMiddleMouseMessage(@NotNull final FriendlyByteBuf buf) { - super(buf, type); this.pos = buf.readBoolean() ? buf.readBlockPos() : null; - this.deltaX = buf.readDouble(); - this.deltaY = buf.readDouble(); + this.delta = buf.readDouble(); this.ctrlKey = buf.readBoolean(); } @Override - protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) + public void toBytes(@NotNull final FriendlyByteBuf buf) { if (this.pos == null) { @@ -79,17 +67,24 @@ protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) buf.writeBoolean(true); buf.writeBlockPos(this.pos); } - buf.writeDouble(this.deltaX); - buf.writeDouble(this.deltaY); + buf.writeDouble(this.delta); buf.writeBoolean(this.ctrlKey); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(@NotNull final NetworkContext ctxIn, final boolean isLogicalServer) { - final ItemStack current = player.getInventory().getSelected(); + final ServerPlayer player = ctxIn.getSender(); + final ItemStack current = player.getInventory().getSelectedItem(); - if (this.deltaX == 0 && this.deltaY == 0) + if (this.delta == 0) { if (current.getItem() instanceof ISpecialBlockPickItem clickableItem) { @@ -100,7 +95,7 @@ protected void onExecute(final IPayloadContext context, final ServerPlayer playe { if (current.getItem() instanceof IScrollableItem scrollableItem) { - scrollableItem.onMouseScroll(player, current, this.deltaX, this.deltaY, this.ctrlKey); + scrollableItem.onMouseScroll(player, current, this.delta, this.ctrlKey); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java index c8c7eed17e..3986e6b02d 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java @@ -1,13 +1,11 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.ClientStructurePackLoader; import com.ldtteam.structurize.storage.StructurePackMeta; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashMap; @@ -16,10 +14,8 @@ /** * Notify the client about the structure packs on the server side. */ -public class NotifyClientAboutStructurePacksMessage extends AbstractClientPlayMessage +public class NotifyClientAboutStructurePacksMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "notify_client_about_structure_packs", NotifyClientAboutStructurePacksMessage::new); - /** * List of server structure packs. * Contains String Name, and Integer version. @@ -29,9 +25,8 @@ public class NotifyClientAboutStructurePacksMessage extends AbstractClientPlayMe /** * Public standard constructor. */ - protected NotifyClientAboutStructurePacksMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public NotifyClientAboutStructurePacksMessage(final FriendlyByteBuf buf) { - super(buf, type); final int length = buf.readInt(); for (int i = 0; i < length; i++) { @@ -45,7 +40,6 @@ protected NotifyClientAboutStructurePacksMessage(final RegistryFriendlyByteBuf b */ public NotifyClientAboutStructurePacksMessage(final Collection clientStructurePacks) { - super(TYPE); for (final StructurePackMeta pack : clientStructurePacks) { this.serverStructurePacks.put(pack.getName(), pack.getVersion()); @@ -53,7 +47,7 @@ public NotifyClientAboutStructurePacksMessage(final Collection packInfo : this.serverStructurePacks.entrySet()) @@ -63,9 +57,19 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final Player player) + public LogicalSide getExecutionSide() { - ClientStructurePackLoader.onServerSyncAttempt(this.serverStructurePacks); + return LogicalSide.CLIENT; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + if (!isLogicalServer) + { + ClientStructurePackLoader.onServerSyncAttempt(this.serverStructurePacks); + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java index 5e5f28b3cc..3de7da11df 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java @@ -1,13 +1,11 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.ServerStructurePackLoader; import com.ldtteam.structurize.storage.StructurePackMeta; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashMap; @@ -16,10 +14,8 @@ /** * Notify the server about the structure packs on the client side. */ -public class NotifyServerAboutStructurePacksMessage extends AbstractServerPlayMessage +public class NotifyServerAboutStructurePacksMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "notify_server_about_structure_packs", NotifyServerAboutStructurePacksMessage::new); - /** * List of client structure packs. * Contains String Name, and Integer version. @@ -29,9 +25,8 @@ public class NotifyServerAboutStructurePacksMessage extends AbstractServerPlayMe /** * Public standard constructor. */ - protected NotifyServerAboutStructurePacksMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public NotifyServerAboutStructurePacksMessage(final FriendlyByteBuf buf) { - super(buf, type); final int length = buf.readInt(); for (int i = 0; i < length; i++) { @@ -45,7 +40,6 @@ protected NotifyServerAboutStructurePacksMessage(final RegistryFriendlyByteBuf b */ public NotifyServerAboutStructurePacksMessage(final Collection clientStructurePacks) { - super(TYPE); for (final StructurePackMeta pack : clientStructurePacks) { this.clientStructurePacks.put(pack.getName(), pack.getVersion()); @@ -53,7 +47,7 @@ public NotifyServerAboutStructurePacksMessage(final Collection packInfo : this.clientStructurePacks.entrySet()) @@ -63,9 +57,19 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public LogicalSide getExecutionSide() { - ServerStructurePackLoader.onClientSyncAttempt(this.clientStructurePacks, player); + return LogicalSide.SERVER; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + if (isLogicalServer) + { + ServerStructurePackLoader.onClientSyncAttempt(this.clientStructurePacks, ctxIn.getSender()); + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java index afe1835805..3d0bb6d5e3 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java @@ -1,68 +1,83 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.client.gui.WindowUndoRedo; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.util.ChangeStorage; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Tuple; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import com.ldtteam.structurize.api.util.Tuple; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class OperationHistoryMessage extends AbstractPlayMessage +public class OperationHistoryMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forBothSides(Constants.MOD_ID, "operation_history", OperationHistoryMessage::new); - /** * List of operations and their IDs */ - private final List> operationIDs; + private List> operationIDs = new ArrayList<>(); /** * Empty constructor used when registering the */ - protected OperationHistoryMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public OperationHistoryMessage(final FriendlyByteBuf buf) { - super(buf, type); - operationIDs = buf.readList(b -> new Tuple<>(b.readUtf(), b.readInt())); + final int count = buf.readInt(); + operationIDs = new ArrayList<>(); + for (int i = 0; i < count; i++) + { + operationIDs.add(new Tuple<>(buf.readUtf(), buf.readInt())); + } } public OperationHistoryMessage() { - super(TYPE); - operationIDs = new ArrayList<>(); + } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { - buf.writeCollection(operationIDs, (b, operation) -> { - b.writeUtf(operation.getA()); - b.writeInt(operation.getB()); - }); + buf.writeInt(operationIDs.size()); + for (final Tuple operation : operationIDs) + { + buf.writeUtf(operation.getA()); + buf.writeInt(operation.getB()); + } } + @Nullable @Override - protected void onClientExecute(final IPayloadContext context, final Player player) + public LogicalSide getExecutionSide() { - WindowUndoRedo.lastOperations = operationIDs; + return null; } @Override - protected void onServerExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final List operations = Manager.getChangeStoragesForPlayer(player.getUUID()); - for (final ChangeStorage storage : operations) + if (isLogicalServer) { - operationIDs.add(new Tuple<>(storage.getOperation().getString(), storage.getID())); - } + if (ctxIn.getSender() == null) + { + return; + } + + final List operations = Manager.getChangeStoragesForPlayer(ctxIn.getSender().getUUID()); + operationIDs = new ArrayList<>(); + for (final ChangeStorage storage : operations) + { + operationIDs.add(new Tuple<>(storage.getOperation().getString(), storage.getID())); + } - this.sendToPlayer(player); + Network.getNetwork().sendToPlayer(this, ctxIn.getSender()); + } + else + { + WindowUndoRedo.lastOperations = operationIDs; + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java index 476a3ccf5b..cdc8eca95a 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java @@ -1,15 +1,13 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.operations.RemoveBlockOperation; import com.ldtteam.structurize.operations.RemoveFilteredOperation; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -17,10 +15,8 @@ /** * Message to remove a block from the world. */ -public class RemoveBlockMessage extends AbstractServerPlayMessage +public class RemoveBlockMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "remove_block", RemoveBlockMessage::new); - /** * The list of items to remove and their positions */ @@ -29,9 +25,8 @@ public class RemoveBlockMessage extends AbstractServerPlayMessage /** * Empty constructor used when registering the message. */ - protected RemoveBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public RemoveBlockMessage(final FriendlyByteBuf buf) { - super(buf, type); final int count = buf.readInt(); for (int i = 0; i < count; i++) @@ -45,7 +40,6 @@ protected RemoveBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessag */ public RemoveBlockMessage(final ItemPositionsStorage itemPositionsStorage) { - super(TYPE); toRemove = List.of(itemPositionsStorage); } @@ -54,12 +48,11 @@ public RemoveBlockMessage(final ItemPositionsStorage itemPositionsStorage) */ public RemoveBlockMessage(final List itemPositionsStorageList) { - super(TYPE); toRemove = itemPositionsStorageList; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(toRemove.size()); for (final ItemPositionsStorage positionsStorage : toRemove) @@ -68,23 +61,30 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } if (toRemove.size() > 1) { - Manager.addToQueue(new RemoveFilteredOperation(player, toRemove)); + Manager.addToQueue(new RemoveFilteredOperation(ctxIn.getSender(), toRemove)); return; } if (!toRemove.isEmpty()) { - Manager.addToQueue(new RemoveBlockOperation(player, toRemove.get(0))); + Manager.addToQueue(new RemoveBlockOperation(ctxIn.getSender(), toRemove.get(0))); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java index 12def2bec6..887bfee17d 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java @@ -1,25 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.operations.RemoveEntityOperation; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.Identifier; import net.minecraft.world.entity.EntityType; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message to remove an entity from the world. */ -public class RemoveEntityMessage extends AbstractServerPlayMessage +public class RemoveEntityMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "remove_entity", RemoveEntityMessage::new); - /** * Position to scan from. */ @@ -33,17 +29,16 @@ public class RemoveEntityMessage extends AbstractServerPlayMessage /** * The entity to remove from the world. */ - private final ResourceLocation entityName; + private final Identifier entityName; /** * Empty constructor used when registering the message. */ - protected RemoveEntityMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public RemoveEntityMessage(final FriendlyByteBuf buf) { - super(buf, type); this.from = buf.readBlockPos(); this.to = buf.readBlockPos(); - this.entityName = buf.readResourceLocation(); + this.entityName = buf.readIdentifier(); } /** @@ -53,34 +48,40 @@ protected RemoveEntityMessage(final RegistryFriendlyByteBuf buf, final PlayMessa * @param pos2 end coordinate. * @param entityName the entity to remove. */ - public RemoveEntityMessage(final BlockPos pos1, final BlockPos pos2, final ResourceLocation entityName) + public RemoveEntityMessage(final BlockPos pos1, final BlockPos pos2, final Identifier entityName) { - super(TYPE); this.from = pos1; this.to = pos2; this.entityName = entityName; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBlockPos(from); buf.writeBlockPos(to); - buf.writeResourceLocation(entityName); + buf.writeIdentifier(entityName); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; } @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } - final EntityType type = BuiltInRegistries.ENTITY_TYPE.get(entityName); + final EntityType type = BuiltInRegistries.ENTITY_TYPE.getValue(entityName); if (type != null) { - Manager.addToQueue(new RemoveEntityOperation(player, from, to, type)); + Manager.addToQueue(new RemoveEntityOperation(ctxIn.getSender(), from, to, type)); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java index 4016b9dc2d..0650a50239 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java @@ -1,24 +1,20 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.operations.ReplaceBlockOperation; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message to replace a block from the world with another one. */ -public class ReplaceBlockMessage extends AbstractServerPlayMessage +public class ReplaceBlockMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "replace_block", ReplaceBlockMessage::new); - /** * The block to replace with its positions */ @@ -37,10 +33,9 @@ public class ReplaceBlockMessage extends AbstractServerPlayMessage /** * Empty constructor used when registering the message. */ - protected ReplaceBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ReplaceBlockMessage(final FriendlyByteBuf buf) { - super(buf, type); - this.blockTo = ItemStackUtils.deserializeFromBuffer(buf); + this.blockTo = ItemStackNbtHelper.readNetworkStack(buf); this.pct = buf.readInt(); toReplace = new ItemPositionsStorage(buf); } @@ -52,28 +47,34 @@ protected ReplaceBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessa */ public ReplaceBlockMessage(final ItemPositionsStorage toReplace, final ItemStack blockTo, final int pct) { - super(TYPE); this.toReplace = toReplace; this.blockTo = blockTo; this.pct = pct; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { - ItemStackUtils.serializeToBuffer(blockTo, buf); + ItemStackNbtHelper.writeNetworkStack(buf, blockTo); buf.writeInt(pct); toReplace.serialize(buf); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } - Manager.addToQueue(new ReplaceBlockOperation(player, toReplace, blockTo, pct)); + Manager.addToQueue(new ReplaceBlockOperation(ctxIn.getSender(), toReplace, blockTo, pct)); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java index 576a0fd81a..ca21a1fead 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java @@ -1,49 +1,41 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.storage.ClientStructurePackLoader; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.io.IOException; /** * Handles sendScanMessages. */ -public class SaveScanMessage extends AbstractClientPlayMessage +public class SaveScanMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "save_scan", SaveScanMessage::new); - private static final String TAG_MILLIS = "millies"; public static final String TAG_SCHEMATIC = "schematic"; - private final CompoundTag compoundNBT; - private final String fileName; + private CompoundTag compoundNBT; + private String fileName; /** * Public standard constructor. */ - protected SaveScanMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SaveScanMessage(final FriendlyByteBuf buf) { - super(buf, type); final FriendlyByteBuf buffer = new FriendlyByteBuf(buf); - CompoundTag tag = null; - String name = null; try (ByteBufInputStream stream = new ByteBufInputStream(buffer)) { - final CompoundTag wrapperCompound = NbtIo.read(stream, NbtAccounter.unlimitedHeap()); - tag = wrapperCompound.getCompound(TAG_SCHEMATIC); - name = wrapperCompound.getString(TAG_MILLIS); + final CompoundTag wrapperCompound = NbtIo.readCompressed(stream, NbtAccounter.unlimitedHeap()); + this.compoundNBT = wrapperCompound.getCompoundOrEmpty(TAG_SCHEMATIC); + this.fileName = wrapperCompound.getStringOr(TAG_MILLIS, ""); } catch (final RuntimeException e) { @@ -53,8 +45,6 @@ protected SaveScanMessage(final RegistryFriendlyByteBuf buf, final PlayMessageTy { Log.getLogger().info("Problem at retrieving structure on server.", e); } - this.compoundNBT = tag; - this.fileName = name; } /** @@ -65,13 +55,12 @@ protected SaveScanMessage(final RegistryFriendlyByteBuf buf, final PlayMessageTy */ public SaveScanMessage(final CompoundTag CompoundNBT, final String fileName) { - super(TYPE); this.fileName = fileName; this.compoundNBT = CompoundNBT; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { final CompoundTag wrapperCompound = new CompoundTag(); wrapperCompound.putString(TAG_MILLIS, fileName); @@ -80,7 +69,7 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) final FriendlyByteBuf buffer = new FriendlyByteBuf(buf); try (ByteBufOutputStream stream = new ByteBufOutputStream(buffer)) { - NbtIo.write(wrapperCompound, stream); + NbtIo.writeCompressed(wrapperCompound, stream); } catch (final IOException e) { @@ -88,12 +77,19 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { if (compoundNBT != null) { - ClientStructurePackLoader.handleSaveScanMessage(compoundNBT, fileName, player.level().registryAccess()); + ClientStructurePackLoader.handleSaveScanMessage(compoundNBT, fileName); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java index bc680441c3..5ef419d915 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java @@ -1,21 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; import com.ldtteam.structurize.items.ItemScanTool; import com.ldtteam.structurize.util.ScanToolData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; /** * Send the scan message for a player to the server. */ -public class ScanOnServerMessage extends AbstractServerPlayMessage +public class ScanOnServerMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "scan_on_server", ScanOnServerMessage::new); - /** * Scan data. */ @@ -29,30 +29,45 @@ public class ScanOnServerMessage extends AbstractServerPlayMessage /** * Empty public constructor. */ - protected ScanOnServerMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ScanOnServerMessage(final FriendlyByteBuf buf) { - super(buf, type); - this.slot = ScanToolData.Slot.STREAM_CODEC.decode(buf); + final String name = buf.readUtf(32767); + final BlockPos from = buf.readBlockPos(); + final BlockPos to = buf.readBlockPos(); + final Optional anchorPos = buf.readBoolean() ? Optional.of(buf.readBlockPos()) : Optional.empty(); + + this.slot = new ScanToolData.Slot(name, new BoxPreviewData(from, to, anchorPos)); this.saveEntities = buf.readBoolean(); } public ScanOnServerMessage(final ScanToolData.Slot slot, final boolean saveEntities) { - super(TYPE); this.slot = slot; this.saveEntities = saveEntities; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { - ScanToolData.Slot.STREAM_CODEC.encode(buf, slot); + buf.writeUtf(slot.getName()); + buf.writeBlockPos(slot.getBox().getPos1()); + buf.writeBlockPos(slot.getBox().getPos2()); + buf.writeBoolean(slot.getBox().getAnchor().isPresent()); + slot.getBox().getAnchor().ifPresent(buf::writeBlockPos); + buf.writeBoolean(saveEntities); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ItemScanTool.saveStructure(player.getCommandSenderWorld(), player, this.slot, saveEntities); + ItemScanTool.saveStructure(ctxIn.getSender().level(), ctxIn.getSender(), this.slot, saveEntities); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java index 201c7eee66..1d9ea8b4ce 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java @@ -1,41 +1,42 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.items.ItemScanTool; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class ScanToolTeleportMessage extends AbstractServerPlayMessage +public class ScanToolTeleportMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "scantool_teleport", ScanToolTeleportMessage::new); - public ScanToolTeleportMessage() { - super(TYPE); } - protected ScanToolTeleportMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ScanToolTeleportMessage(@NotNull final FriendlyByteBuf buf) + { + } + + @Override + public void toBytes(FriendlyByteBuf buf) { - super(buf, type); } + @Nullable @Override - protected void toBytes(RegistryFriendlyByteBuf buf) + public LogicalSide getExecutionSide() { + return LogicalSide.SERVER; } @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final ItemStack stack = player.getMainHandItem(); + final ItemStack stack = ctxIn.getSender().getMainHandItem(); if (stack.getItem() instanceof ItemScanTool tool) { - tool.onTeleport(player, stack); + tool.onTeleport(ctxIn.getSender(), stack); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ServerUUIDMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ServerUUIDMessage.java new file mode 100644 index 0000000000..9b1d11e58d --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/messages/ServerUUIDMessage.java @@ -0,0 +1,49 @@ +package com.ldtteam.structurize.network.messages; + +import com.ldtteam.structurize.management.Manager; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +/** + * Class handling the Server UUID Message. + */ +public class ServerUUIDMessage implements IMessage +{ + private final UUID serverUUID; + + /** + * Empty constructor used when registering the message. + */ + public ServerUUIDMessage() + { + this.serverUUID = Manager.getServerUUID(); + } + + public ServerUUIDMessage(final FriendlyByteBuf buf) + { + this.serverUUID = buf.readUUID(); + } + + @Override + public void toBytes(final FriendlyByteBuf buf) + { + buf.writeUUID(Manager.getServerUUID()); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + Manager.setServerUUID(serverUUID); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java b/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java index bcbd8ba274..1388cb2b97 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java @@ -1,27 +1,24 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.items.ItemTagTool; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import com.ldtteam.structurize.items.ModItems; -import com.ldtteam.structurize.items.ItemTagTool.TagData; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Messages for adding or removing a tag */ -public class SetTagInTool extends AbstractServerPlayMessage +public class SetTagInTool implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "set_tag_in_tool", SetTagInTool::new); - /** * The tag to use */ - private final String tag; + private String tag = ""; /** * The tags blockpos @@ -31,40 +28,50 @@ public class SetTagInTool extends AbstractServerPlayMessage /** * Empty constructor used when registering the */ - protected SetTagInTool(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SetTagInTool(final FriendlyByteBuf buf) { - super(buf, type); this.tag = buf.readUtf(32767); this.slot = buf.readInt(); } public SetTagInTool(final String tag, final int slot) { - super(TYPE); this.slot = slot; this.tag = tag; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeUtf(tag); buf.writeInt(slot); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (ctxIn.getSender() == null) + { + return; + } + + if (!ctxIn.getSender().isCreative()) { - player.displayClientMessage(Component.translatable("structurize.gui.tagtool.creative_only"), false); + ctxIn.getSender().sendSystemMessage(Component.translatable("structurize.gui.tagtool.creative_only")); return; } - final ItemStack stack = player.getInventory().getItem(slot); + final ItemStack stack = ctxIn.getSender().getInventory().getItem(slot); if (stack.getItem() == ModItems.tagTool.get()) { - TagData.updateItemStack(stack, tags -> tags.setCurrentTag(tag)); + ItemStackNbtHelper.getOrCreateCustomTag(stack).putString(ItemTagTool.TAG_CURRENT_TAG, tag); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java index 7ac5c5fa2b..0b2f33f885 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java @@ -1,53 +1,72 @@ -package com.ldtteam.structurize.network.messages; - -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import org.jetbrains.annotations.NotNull; - -/** - * Tells the client to update their scan render box. - */ -public class ShowScanMessage extends AbstractClientPlayMessage -{ - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "show_scan", ShowScanMessage::new); - - private final BoxPreviewData box; - - /** - * Construct from box - * @param box the box to sync - */ - public ShowScanMessage(@NotNull final BoxPreviewData box) - { - super(TYPE); - this.box = box; - } - - /** - * Construct from network - * @param buf the buffer - */ - protected ShowScanMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) - { - super(buf, type); - this.box = BoxPreviewData.STREAM_CODEC.decode(buf); - } - - @Override - protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) - { - BoxPreviewData.STREAM_CODEC.encode(buf, box); - } - - @Override - protected void onExecute(final IPayloadContext context, final Player player) - { - RenderingCache.queue("scan", this.box); - } -} +package com.ldtteam.structurize.network.messages; + +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewRenderTask; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +/** + * Tells the client to update their scan render box. + */ +public class ShowScanMessage implements IMessage +{ + private final BoxPreviewData box; + + /** + * Construct from box + * @param box the box to sync + */ + public ShowScanMessage(@NotNull final BoxPreviewData box) + { + this.box = box; + } + + /** + * Construct from network + * @param buf the buffer + */ + public ShowScanMessage(@NotNull final FriendlyByteBuf buf) + { + final BlockPos from = buf.readBlockPos(); + final BlockPos to = buf.readBlockPos(); + final BlockPos anchor = buf.readBoolean() ? buf.readBlockPos() : null; + + this.box = new BoxPreviewData(from, to, Optional.ofNullable(anchor)); + } + + @Override + public void toBytes(@NotNull final FriendlyByteBuf buf) + { + buf.writeBlockPos(this.box.getPos1()); + buf.writeBlockPos(this.box.getPos2()); + if (this.box.getAnchor().isPresent()) + { + buf.writeBoolean(true); + buf.writeBlockPos(this.box.getAnchor().get()); + } + else + { + buf.writeBoolean(false); + } + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + + @Override + public void onExecute(@NotNull final NetworkContext ctxIn, final boolean isLogicalServer) + { + RenderTaskManager.addRenderTask("scan", new BoxPreviewRenderTask("scan", this.box, 60 * 10)); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java index f61d1ae200..aac1a2dad4 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java @@ -1,23 +1,19 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.rendering.RenderingCache; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.UUID; /** * Sync blueprint preview data to the client. */ -public class SyncPreviewCacheToClient extends AbstractClientPlayMessage +public class SyncPreviewCacheToClient implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "sync_preview_cache_to_client", SyncPreviewCacheToClient::new); - public static final String SHARED_PREFIX = "shared:"; /** @@ -33,9 +29,8 @@ public class SyncPreviewCacheToClient extends AbstractClientPlayMessage /** * Buffer reading message constructor. */ - protected SyncPreviewCacheToClient(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SyncPreviewCacheToClient(final FriendlyByteBuf buf) { - super(buf, type); this.previewData = new BlueprintPreviewData(buf, false); this.playerUUID = buf.readUUID(); } @@ -45,20 +40,26 @@ protected SyncPreviewCacheToClient(final RegistryFriendlyByteBuf buf, final Play */ public SyncPreviewCacheToClient(final BlueprintPreviewData previewData, final UUID playerUUID) { - super(TYPE); this.previewData = previewData; this.playerUUID = playerUUID; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { this.previewData.writeToBuf(buf); buf.writeUUID(this.playerUUID); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { final String uuid = SHARED_PREFIX + playerUUID.toString(); if (previewData.isEmpty()) diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java index 87ff4736fb..c2b133902a 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java @@ -1,21 +1,17 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.rendering.ServerPreviewDistributor; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Sync blueprint preview data to the server. */ -public class SyncPreviewCacheToServer extends AbstractServerPlayMessage +public class SyncPreviewCacheToServer implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "sync_preview_cache_to_server", SyncPreviewCacheToServer::new); - /** * The preview data. */ @@ -24,9 +20,8 @@ public class SyncPreviewCacheToServer extends AbstractServerPlayMessage /** * Buffer reading message constructor. */ - protected SyncPreviewCacheToServer(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SyncPreviewCacheToServer(final FriendlyByteBuf buf) { - super(buf, type); this.previewData = new BlueprintPreviewData(buf); } @@ -35,19 +30,25 @@ protected SyncPreviewCacheToServer(final RegistryFriendlyByteBuf buf, final Play */ public SyncPreviewCacheToServer(final BlueprintPreviewData previewData) { - super(TYPE); this.previewData = previewData; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { this.previewData.writeToBuf(buf); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ServerPreviewDistributor.distribute(this.previewData, player); + ServerPreviewDistributor.distribute(this.previewData, ctxIn.getSender()); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java b/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java index 31d6810331..af251282a3 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java @@ -1,29 +1,25 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.rendering.ServerPreviewDistributor; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import com.ldtteam.structurize.api.util.Tuple; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Sync player settings to server. */ -public class SyncSettingsToServer extends AbstractServerPlayMessage +public class SyncSettingsToServer implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "sync_settings_to_server", SyncSettingsToServer::new); - private final boolean displayShared; /** * Buffer reading message constructor. */ - protected SyncSettingsToServer(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SyncSettingsToServer(final FriendlyByteBuf buf) { - super(buf, type); this.displayShared = buf.readBoolean(); } @@ -32,19 +28,25 @@ protected SyncSettingsToServer(final RegistryFriendlyByteBuf buf, final PlayMess */ public SyncSettingsToServer() { - super(TYPE); this.displayShared = Structurize.getConfig().getClient().displayShared.get(); } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBoolean(displayShared); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ServerPreviewDistributor.register(player, displayShared); + ServerPreviewDistributor.register(ctxIn.getSender(), displayShared); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java b/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java index bca4dd8bd4..4f679a5f44 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java @@ -1,23 +1,18 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.ClientStructurePackLoader; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Transfer a zipped structure pack to the client. */ -public class TransferStructurePackToClient extends AbstractClientPlayMessage +public class TransferStructurePackToClient implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "transfer_structure_pack_to_client", TransferStructurePackToClient::new); - /** * Payload of the message (to transfer to client). */ @@ -36,13 +31,11 @@ public class TransferStructurePackToClient extends AbstractClientPlayMessage /** * Public standard constructor. */ - protected TransferStructurePackToClient(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public TransferStructurePackToClient(final FriendlyByteBuf buf) { - super(buf, type); this.packname = buf.readUtf(32767); this.eol = buf.readBoolean(); - final int size = buf.readInt(); - this.payload = Unpooled.wrappedBuffer(buf.readBytes(size)); + this.payload = Unpooled.wrappedBuffer(buf.readByteArray()); } /** @@ -53,26 +46,33 @@ protected TransferStructurePackToClient(final RegistryFriendlyByteBuf buf, final */ public TransferStructurePackToClient(final String packName, final ByteBuf payload, final boolean eol) { - super(TYPE); this.packname = packName; this.payload = payload; this.eol = eol; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeUtf(this.packname); buf.writeBoolean(this.eol); - final int size = this.payload.readableBytes(); - buf.writeInt(size); - buf.writeBytes(this.payload, size); - this.payload.resetReaderIndex(); + buf.writeByteArray(this.payload.array()); + this.payload.release(); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; } @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ClientStructurePackLoader.onStructurePackTransfer(this.packname, this.payload, this.eol); + if (!isLogicalServer) + { + ClientStructurePackLoader.onStructurePackTransfer(this.packname, this.payload, this.eol); + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java index d3420ce843..f78e845470 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java @@ -1,20 +1,16 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.management.Manager; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message class which handles undoing a change to the world. */ -public class UndoRedoMessage extends AbstractServerPlayMessage +public class UndoRedoMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "undo_redo", UndoRedoMessage::new); - private final int id; private final boolean undo; @@ -23,40 +19,45 @@ public class UndoRedoMessage extends AbstractServerPlayMessage */ public UndoRedoMessage(final int id, final boolean undo) { - super(TYPE); this.undo = undo; this.id = id; } - protected UndoRedoMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public UndoRedoMessage(final FriendlyByteBuf buf) { - super(buf, type); this.id = buf.readInt(); this.undo = buf.readBoolean(); } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(id); buf.writeBoolean(undo); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } if (undo) { - Manager.undo(player, id); + Manager.undo(ctxIn.getSender(), id); } else { - Manager.redo(player, id); + Manager.redo(ctxIn.getSender(), id); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java b/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java index 0480638ded..27b33e2d7c 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java @@ -1,21 +1,19 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Marks an area of blocks for re-rendering on the client */ -public class UpdateClientRender extends AbstractClientPlayMessage +public class UpdateClientRender implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "update_client_render", UpdateClientRender::new); - /** * Position to scan from. */ @@ -29,9 +27,8 @@ public class UpdateClientRender extends AbstractClientPlayMessage /** * Empty public constructor. */ - protected UpdateClientRender(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public UpdateClientRender(final FriendlyByteBuf buf) { - super(buf, type); this.from = buf.readBlockPos(); this.to = buf.readBlockPos(); } @@ -43,22 +40,35 @@ protected UpdateClientRender(final RegistryFriendlyByteBuf buf, final PlayMessag */ public UpdateClientRender(final BlockPos from, final BlockPos to) { - super(TYPE); this.from = from; this.to = to; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBlockPos(from); buf.writeBlockPos(to); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + @SuppressWarnings("resource") @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - Minecraft.getInstance().levelRenderer.setBlocksDirty(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); + if (!isLogicalServer) + { + final ClientLevel level = Minecraft.getInstance().level; + for (BlockPos pos : BlockPos.betweenClosed(from, to)) + { + level.sendBlockUpdated(pos, level.getBlockState(pos), level.getBlockState(pos), Block.UPDATE_ALL); + } + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java index 366bc1c301..778ac7f9b1 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java @@ -1,59 +1,64 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.items.ItemScanTool; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import com.ldtteam.structurize.util.ScanToolData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Send the scan tool update message to the client. */ -public class UpdateScanToolMessage extends AbstractServerPlayMessage +public class UpdateScanToolMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "update_scan_tool", UpdateScanToolMessage::new); - - private final ScanToolData data; + /** + * Data. + */ + private final CompoundTag tag; /** * Empty public constructor. */ - protected UpdateScanToolMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public UpdateScanToolMessage(final FriendlyByteBuf buf) { - super(buf, type); - - this.data = ScanToolData.STREAM_CODEC.decode(buf); + this.tag = buf.readNbt(); } /** * Update the scan tool. * @param data the new data */ - public UpdateScanToolMessage(final ScanToolData data) + public UpdateScanToolMessage(@NotNull ScanToolData data) { - super(TYPE); + this.tag = data.getInternalTag().copy(); + } - this.data = data; + @Override + public void toBytes(final FriendlyByteBuf buf) + { + buf.writeNbt(this.tag); } + @Nullable @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public LogicalSide getExecutionSide() { - ScanToolData.STREAM_CODEC.encode(buf, this.data); + return LogicalSide.SERVER; } @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final ItemStack stack = player.getMainHandItem(); + final ItemStack stack = ctxIn.getSender().getMainHandItem(); if (stack.getItem() instanceof ItemScanTool tool) { - // normally you should sanity check client data more, but there's nothing particularly abuse-prone here - tool.loadSlot(ScanToolData.updateItemStack(stack, old -> this.data), stack); + ItemStackNbtHelper.setCustomTag(stack, this.tag); + tool.loadSlot(new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)), stack); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/splitting/SplitPacketMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/splitting/SplitPacketMessage.java new file mode 100644 index 0000000000..70f14a9224 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/messages/splitting/SplitPacketMessage.java @@ -0,0 +1,136 @@ +package com.ldtteam.structurize.network.messages.splitting; + +import com.google.common.collect.Maps; +import com.google.common.primitives.Bytes; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.network.NetworkChannel; +import com.ldtteam.structurize.network.messages.IMessage; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.ExecutionException; + +/** + * Represents a class that wrappers other messages in byte form and is used to split the wrapped messages data into several chunks. + */ +public class SplitPacketMessage implements IMessage +{ + /** + * Internal communication id. Used to indicate to what wrapped message this belongs to. + */ + private int communicationId = -1; + + /** + * The index of the split message in the wrapped message. + */ + private int packetIndex = -1; + + /** + * Indicates if this is the last message in the chain. + */ + private boolean terminator = false; + + /** + * The id of the message inside the splitting logic. Identical to the index codec system in SimpleChannel- + */ + private int innerMessageId = -1; + + /** + * The payload. + */ + private byte[] payload; + + /** + * The network receiving constructor. + */ + public SplitPacketMessage(final FriendlyByteBuf buf) + { + this.communicationId = buf.readVarInt(); + this.packetIndex = buf.readVarInt(); + this.terminator = buf.readBoolean(); + this.innerMessageId = buf.readVarInt(); + this.payload = buf.readByteArray(); + } + + public SplitPacketMessage(final int communicationId, final int packetIndex, final boolean terminator, final int innerMessageId, final byte[] payload) + { + this.communicationId = communicationId; + this.packetIndex = packetIndex; + this.terminator = terminator; + this.innerMessageId = innerMessageId; + this.payload = payload; + } + + @Override + public void toBytes(final FriendlyByteBuf buf) + { + buf.writeVarInt(this.communicationId); + buf.writeVarInt(this.packetIndex); + buf.writeBoolean(this.terminator); + buf.writeVarInt(this.innerMessageId); + buf.writeByteArray(this.payload); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return null; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + try + { + //Sync on the message cache since this is still on the Netty thread. + synchronized (Network.getNetwork().getMessageCache()) + { + Network.getNetwork().getMessageCache().get(this.communicationId, Maps::newConcurrentMap).put(this.packetIndex, this.payload); + } + + if (!this.terminator) + { + //We are not the last message stop executing. + return; + } + + //No need to sync again, since we are now the last packet to arrive. + //All data gets sorted and appended. + final byte[] packetData = Network.getNetwork().getMessageCache().get(this.communicationId, Maps::newConcurrentMap).entrySet() + .stream() + .sorted(Map.Entry.comparingByKey()) + .map(Map.Entry::getValue) + .reduce(new byte[0], Bytes::concat); + + //Grab the entry from the inner message id. + final NetworkChannel.NetworkingMessageEntry messageEntry = Network.getNetwork().getMessagesTypes().get(this.innerMessageId); + + //Create a new buffer that reads from the packet data and then deserialize the inner message. + final ByteBuf buffer = Unpooled.wrappedBuffer(packetData); + //Create a message. + final IMessage message = messageEntry.getCreator().apply(new FriendlyByteBuf(buffer)); + buffer.release(); + + //Execute the message. + final LogicalSide packetOrigin = ctxIn.isClientOrigin() ? LogicalSide.CLIENT : LogicalSide.SERVER; + if (message.getExecutionSide() != null && packetOrigin.equals(message.getExecutionSide())) + { + Log.getLogger().warn("Receving {} at wrong side!", message.getClass().getName()); + return; + } + // boolean param MUST equals true if packet arrived at logical server + ctxIn.enqueueWork(() -> message.onExecute(ctxIn, packetOrigin == LogicalSide.SERVER)); + } + catch (ExecutionException e) + { + Log.getLogger().error("Failed to handle split packet.", e); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java b/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java index ce3dffdec2..ad5daa173a 100644 --- a/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java @@ -1,5 +1,6 @@ package com.ldtteam.structurize.operations; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.network.messages.UpdateClientRender; import com.ldtteam.structurize.util.ChangeStorage; @@ -100,7 +101,7 @@ public final boolean apply(final ServerLevel world) } } - new UpdateClientRender(startPos, endPos).sendToAllClients(); + Network.getNetwork().sendToEveryone(new UpdateClientRender(startPos, endPos)); return true; } diff --git a/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java b/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java index f1fcd63fa8..b3bcb9dc60 100644 --- a/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java @@ -1,7 +1,6 @@ package com.ldtteam.structurize.operations; import com.ldtteam.structurize.util.ChangeStorage; -import com.ldtteam.structurize.util.ITickedWorldOperation; import org.jetbrains.annotations.NotNull; /** diff --git a/src/main/java/com/ldtteam/structurize/util/ITickedWorldOperation.java b/src/main/java/com/ldtteam/structurize/operations/ITickedWorldOperation.java similarity index 83% rename from src/main/java/com/ldtteam/structurize/util/ITickedWorldOperation.java rename to src/main/java/com/ldtteam/structurize/operations/ITickedWorldOperation.java index e4be611aec..64a41c96b0 100644 --- a/src/main/java/com/ldtteam/structurize/util/ITickedWorldOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/ITickedWorldOperation.java @@ -1,5 +1,6 @@ -package com.ldtteam.structurize.util; +package com.ldtteam.structurize.operations; +import com.ldtteam.structurize.util.ChangeStorage; import net.minecraft.server.level.ServerLevel; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java b/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java index 839595161c..0578e892bd 100644 --- a/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java @@ -13,7 +13,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Objects; import java.util.UUID; import static com.ldtteam.structurize.placement.AbstractBlueprintIterator.NULL_POS; @@ -47,7 +46,7 @@ public class PlaceStructureOperation extends BaseOperation */ public PlaceStructureOperation(@NotNull final StructurePlacer placer, @Nullable final Player player) { - super(new ChangeStorage(Component.translatable("com.ldtteam.structurize.place_structure", Objects.requireNonNullElse(placer.getHandler().getBluePrint().getName(), "[NULL]")), + super(new ChangeStorage(Component.translatable("com.ldtteam.structurize.place_structure", placer.getHandler().getBluePrint().getName()), player != null ? player.getUUID() : UUID.randomUUID())); this.placer = placer; this.currentPos = NULL_POS; @@ -56,7 +55,7 @@ public PlaceStructureOperation(@NotNull final StructurePlacer placer, @Nullable @Override public boolean apply(final ServerLevel world) { - if (placer.isReady() && placer.getHandler().getWorld().dimension().location().equals(world.dimension().location())) + if (placer.isReady() && placer.getHandler().getWorld().dimension().identifier().equals(world.dimension().identifier())) { StructurePhasePlacementResult result; switch (structurePhase) diff --git a/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java b/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java index 43b71284f0..ccdc036752 100644 --- a/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java @@ -9,6 +9,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import java.util.List; import java.util.UUID; @@ -52,8 +53,10 @@ public RemoveEntityOperation(final Player player, final BlockPos startPos, final @Override public boolean apply(final ServerLevel world) { - final List list = world.getEntitiesOfClass(Entity.class, AABB.encapsulatingFullBlocks(startPos, endPos)); - storage.addEntities(list, world.registryAccess()); + final List list = world.getEntitiesOfClass( + Entity.class, + new AABB(Vec3.atLowerCornerOf(startPos), Vec3.atLowerCornerOf(endPos))); + storage.addEntities(list); int count = 0; for (final Entity entity : list) diff --git a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java index 290adfddf3..2e86986490 100644 --- a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java +++ b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java @@ -4,7 +4,8 @@ import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.BlueprintPositionInfo; import net.minecraft.core.BlockPos; -import net.neoforged.neoforge.common.util.TriPredicate; +import com.ldtteam.structurize.api.util.TriPredicate; + import java.util.Collections; import java.util.function.Supplier; diff --git a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java index 52f3e94cb0..fde4568cf4 100644 --- a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java +++ b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java @@ -1,7 +1,9 @@ package com.ldtteam.structurize.placement; +import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.BlueprintPositionInfo; import net.minecraft.core.BlockPos; +import com.ldtteam.structurize.api.util.TriPredicate; /** * This is a base class for BlueprintIterators based on a delegated iterator diff --git a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java index 3726d7df46..56b5866043 100644 --- a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java +++ b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.BlockPosUtil; +import com.ldtteam.structurize.api.util.BlockPosUtil; import com.ldtteam.structurize.placement.structure.IStructureHandler; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java index 9d670d1e81..d7bbf55abe 100644 --- a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java +++ b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.placement.structure.IStructureHandler; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java b/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java index 1d6a8daca7..43174e7138 100644 --- a/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java +++ b/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java @@ -3,7 +3,7 @@ import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.BlueprintPositionInfo; import net.minecraft.core.BlockPos; -import net.neoforged.neoforge.common.util.TriPredicate; +import com.ldtteam.structurize.api.util.TriPredicate; public interface IBlueprintIterator { diff --git a/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java b/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java index 97aa9cdf60..27c334d0a6 100644 --- a/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java +++ b/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -14,7 +14,7 @@ public interface IPlacementContext * Getter for the placement settings. * @return the settings object. */ - RotationMirror getRotationMirror(); + PlacementSettings getRotationMirror(); /** * If this is supposed to be fancy placement (player facing) or builder facing (complete). diff --git a/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java index 4e30d96254..9c3be75109 100644 --- a/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java +++ b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; @@ -22,16 +22,16 @@ public class SimplePlacementContext implements IPlacementContext /** * Rotation mirror. */ - private final RotationMirror rotationMirror; + private final PlacementSettings rotationMirror; - public SimplePlacementContext(final boolean fancyPlacement, final RotationMirror rotationMirror) + public SimplePlacementContext(final boolean fancyPlacement, final PlacementSettings rotationMirror) { this.fancyPlacement = fancyPlacement; this.rotationMirror = rotationMirror; } @Override - public RotationMirror getRotationMirror() + public PlacementSettings getRotationMirror() { return rotationMirror; } diff --git a/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java b/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java index 8cbc8918f9..78ab53285e 100644 --- a/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java +++ b/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java @@ -4,11 +4,14 @@ import com.ldtteam.structurize.operations.PlaceStructureOperation; import com.ldtteam.structurize.placement.structure.CreativeStructureHandler; import com.ldtteam.structurize.placement.structure.IStructureHandler; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.management.Manager; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.world.level.block.AirBlock; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; @@ -26,10 +29,10 @@ public class StructurePlacementUtils * @param rotation the rotation. * @param mirror the mirror. */ - public static void unloadStructure(final Level world, final BlockPos startPos, final Blueprint blueprint, final RotationMirror rotMir) + public static void unloadStructure(final Level world, final BlockPos startPos, final Blueprint blueprint, final Rotation rotation, final Mirror mirror) { - final IStructureHandler structure = new CreativeStructureHandler(world, startPos, blueprint, rotMir, false); - structure.getBluePrint().setRotationMirror(rotMir, world); + final IStructureHandler structure = new CreativeStructureHandler(world, startPos, blueprint, new PlacementSettings(mirror, rotation), false); + structure.getBluePrint().setRotationMirror(RotationMirror.of(rotation, mirror), world); final StructurePlacer placer = new StructurePlacer(structure); placer.executeStructureStep(world, null, new BlockPos(0, 0, 0), StructurePlacer.Operation.BLOCK_REMOVAL, @@ -43,24 +46,26 @@ public static void unloadStructure(final Level world, final BlockPos startPos, f * @param worldObj the world to load it in * @param blueprint the structures blueprint * @param pos coordinates - * @param rotMir the rotation and the mirror used. + * @param rotation the rotation. + * @param mirror the mirror used. * @param fancyPlacement if fancy or complete. * @param player the placing player. */ public static void loadAndPlaceStructureWithRotation( final Level worldObj, final Blueprint blueprint, - final BlockPos pos, final RotationMirror rotMir, + final BlockPos pos, final Rotation rotation, + final Mirror mirror, final boolean fancyPlacement, final ServerPlayer player) { try { - final IStructureHandler structure = new CreativeStructureHandler(worldObj, pos, blueprint, rotMir, fancyPlacement); + final IStructureHandler structure = new CreativeStructureHandler(worldObj, pos, blueprint, new PlacementSettings(mirror, rotation), fancyPlacement); if (fancyPlacement) { structure.fancyPlacement(); } - structure.getBluePrint().setRotationMirror(rotMir, worldObj); + structure.getBluePrint().setRotationMirror(RotationMirror.of(rotation, mirror), worldObj); final StructurePlacer instantPlacer = new StructurePlacer(structure); Manager.addToQueue(new PlaceStructureOperation(instantPlacer, player)); diff --git a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java index 2a8918eaf5..99289c5ac0 100644 --- a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java +++ b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java @@ -1,8 +1,8 @@ package com.ldtteam.structurize.placement; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; @@ -15,17 +15,14 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Display; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.decoration.HangingEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.AirBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.BucketPickup; -import net.minecraft.world.level.block.DoublePlantBlock; -import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; @@ -36,7 +33,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.function.Supplier; @@ -173,7 +169,7 @@ public StructurePhasePlacementResult executeStructureStep( requiredItems.addAll(result.getRequiredItems()); break; case SPAWN_ENTITY: - result = handleEntitySpawn(world, worldPos, localPos, storage, false); + result = handleEntitySpawn(world, worldPos, localPos, storage); break; default: result = handleBlockPlacement(world, worldPos, storage, new BlockInfo(localPos, localState, handler.getBluePrint().getTileEntityData(worldPos, localPos))); @@ -243,11 +239,83 @@ public BlockPlacementResult handleBlockPlacement( } } - // TODO 1.22: Remove from here and transition our AIs to entity placement. - final BlockPlacementResult entityResult = handleEntitySpawn(world, worldPos, localPos, storage, false); - if (!entityResult.getResult().equals(BlockPlacementResult.Result.SUCCESS)) + // todo remove entity placement from here in the future. + for (final CompoundTag compound : this.iterator.getBluePrintPositionInfo(localPos).getEntities()) { - return entityResult; + if (compound != null) + { + try + { + final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); + + final Entity entity = EntityType.loadEntityRecursive( + compound, + world, + new EntitySpawnRequest(EntitySpawnReason.STRUCTURE, false), + loaded -> loaded); + + if (entity != null) + { + entity.setUUID(UUID.randomUUID()); + Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); + if (entity instanceof HangingEntity hang) + { + posInWorld = posInWorld.subtract(Vec3.atLowerCornerOf(hang.blockPosition().subtract(hang.getPos()))); + } + entity.setPos(posInWorld.x, posInWorld.y, posInWorld.z); + entity.setYRot(entity.getYRot()); + entity.setXRot(entity.getXRot()); + + final List list = world.getEntitiesOfClass(entity.getClass(), new AABB(posInWorld.add(1,1,1), posInWorld.add(-1,-1,-1))); + boolean foundEntity = false; + for (Entity worldEntity: list) + { + if (worldEntity.position().equals(posInWorld)) + { + foundEntity = true; + break; + } + } + + if (foundEntity || (entity instanceof Mob && !handler.isCreative())) + { + continue; + } + + List requiredItems = ItemStackUtils.getListOfStackForEntity(entity, pos); + if (!handler.isCreative()) + { + if (requiredItems == null) + { + // Only handle entities we explicitly know how to handle. + continue; + } + + if (!this.handler.hasRequiredItems(requiredItems)) + { + return new BlockPlacementResult(worldPos, BlockPlacementResult.Result.MISSING_ITEMS, requiredItems); + } + } + else if (requiredItems == null) + { + requiredItems = new ArrayList<>(); + } + + world.addFreshEntity(entity); + if (storage != null) + { + storage.addToBeKilledEntity(entity); + } + + this.handler.consume(requiredItems); + this.handler.triggerEntitySuccess(localPos, requiredItems, true); + } + } + catch (final RuntimeException e) + { + Log.getLogger().info("Couldn't restore entity", e); + } + } } if (IPlacementHandler.doesWorldStateMatchBlueprintState(blockInfo, worldPos, this.handler)) @@ -322,8 +390,7 @@ public BlockPlacementResult handleEntitySpawn( final Level world, final BlockPos worldPos, final BlockPos localPos, - final ChangeStorage storage, - final boolean simulate) + final ChangeStorage storage) { for (final CompoundTag compound : this.iterator.getBluePrintPositionInfo(localPos).getEntities()) { @@ -333,22 +400,24 @@ public BlockPlacementResult handleEntitySpawn( { final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); - final Optional> type = EntityType.by(compound); - if (type.isPresent()) - { - final Entity entity = type.get().create(world); - if (entity != null) - { - entity.load(compound); + final Entity entity = EntityType.loadEntityRecursive( + compound, + world, + new EntitySpawnRequest(EntitySpawnReason.STRUCTURE, false), + loaded -> loaded); + if (entity != null) + { entity.setUUID(UUID.randomUUID()); - final Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); - Vec3 moveToPos = posInWorld; + Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); if (entity instanceof HangingEntity hang) { - moveToPos = posInWorld.subtract(Vec3.atLowerCornerOf(hang.blockPosition().subtract(hang.getPos()))); + posInWorld = posInWorld.subtract(Vec3.atLowerCornerOf(hang.blockPosition().subtract(hang.getPos()))); } - entity.moveTo(moveToPos.x, moveToPos.y, moveToPos.z, entity.getYRot(), entity.getXRot()); + entity.setPos(posInWorld.x, posInWorld.y, posInWorld.z); + entity.setYRot(entity.getYRot()); + entity.setXRot(entity.getXRot()); + final List list = world.getEntitiesOfClass(entity.getClass(), new AABB(posInWorld.add(1,1,1), posInWorld.add(-1,-1,-1))); boolean foundEntity = false; for (Entity worldEntity: list) @@ -370,8 +439,8 @@ public BlockPlacementResult handleEntitySpawn( continue; } - List requiredItems = ItemStackUtils.getListOfStackForEntity(entity); - if (!handler.isCreative() || simulate) + List requiredItems = ItemStackUtils.getListOfStackForEntity(entity, pos); + if (!handler.isCreative()) { if (requiredItems == null) { @@ -379,7 +448,7 @@ public BlockPlacementResult handleEntitySpawn( continue; } - if (simulate || !this.handler.hasRequiredItems(requiredItems)) + if (!this.handler.hasRequiredItems(requiredItems)) { return new BlockPlacementResult(worldPos, BlockPlacementResult.Result.MISSING_ITEMS, requiredItems); } @@ -389,18 +458,15 @@ else if (requiredItems == null) requiredItems = new ArrayList<>(); } - if (!simulate) - { - world.addFreshEntity(entity); - this.handler.consume(requiredItems); - this.handler.triggerEntitySuccess(localPos, requiredItems, true); - } + world.addFreshEntity(entity); if (storage != null) { storage.addToBeKilledEntity(entity); } + + this.handler.consume(requiredItems); + this.handler.triggerEntitySuccess(localPos, requiredItems, true); } - } } catch (final RuntimeException e) { @@ -494,10 +560,47 @@ public BlockPlacementResult getResourceRequirements( } final List requiredItems = new ArrayList<>(); - final BlockPlacementResult result = handleEntitySpawn(world, worldPos, localPos, null, true); - if (result.getResult().equals(BlockPlacementResult.Result.MISSING_ITEMS)) + for (final CompoundTag compound : iterator.getBluePrintPositionInfo(localPos).getEntities()) { - requiredItems.addAll(result.getRequiredItems()); + if (compound != null) + { + try + { + final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); + + final Entity entity = EntityType.loadEntityRecursive( + compound, + world, + new EntitySpawnRequest(EntitySpawnReason.STRUCTURE, false), + loaded -> loaded); + + if (entity != null) + { + final Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); + final List list = world.getEntitiesOfClass(entity.getClass(), new AABB(posInWorld.add(1,1,1), posInWorld.add(-1,-1,-1))); + boolean foundEntity = false; + for (Entity worldEntity: list) + { + if (worldEntity.position().equals(posInWorld)) + { + foundEntity = true; + break; + } + } + + if (foundEntity) + { + continue; + } + + requiredItems.addAll(ItemStackUtils.getListOfStackForEntity(entity, pos)); + } + } + catch (final RuntimeException e) + { + Log.getLogger().info("Couldn't restore entity", e); + } + } } if (IPlacementHandler.doesWorldStateMatchBlueprintState(new BlockInfo(localPos, localState, handler.getBluePrint().getTileEntityData(worldPos, localPos)), worldPos, this.handler)) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java index 09c03d4296..088cd7b7d7 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java @@ -1,14 +1,16 @@ package com.ldtteam.structurize.placement.handlers.placement; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.util.BlockUtils; import net.minecraft.core.BlockPos; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -20,7 +22,7 @@ import java.util.Collections; import java.util.List; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.getItemsFromTileEntity; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; @@ -47,10 +49,10 @@ public ActionProcessingResult handle( { if (tileEntityData != null && BlockEntity.loadStatic(pos, blockState, tileEntityData, world.registryAccess()) instanceof BlockEntityTagSubstitution tagEntity) { - final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().blockState()); + final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().getBlockState()); if (placementHandler != this) { - return placementHandler.handle(world, pos, tagEntity.getReplacement().blockState(), tagEntity.getReplacement().serializedBE().orElse(null), placementContext); + return placementHandler.handle(world, pos, tagEntity.getReplacement().getBlockState(), tagEntity.getReplacement().getBlockEntityTag(), placementContext); } else { @@ -73,7 +75,7 @@ public ActionProcessingResult handle( world.setBlock(pos, blockState, UPDATE_FLAG); if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); } return ActionProcessingResult.PASS; } @@ -85,7 +87,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); } } @@ -97,21 +99,25 @@ public boolean doesWorldStateMatchBlueprintState(final BlockState worldState, fi { if (placementContext.fancyPlacement()) { - if (blockEntityData != null && BlockEntity.loadStatic(BlockPos.ZERO, blueprintState, blockEntityData.getB(), placementContext.getBluePrint().getRegistryAccess()) instanceof BlockEntityTagSubstitution tagEntity) + if (blockEntityData != null && BlockEntity.loadStatic( + BlockPos.ZERO, + blueprintState, + blockEntityData.getB(), + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)) instanceof BlockEntityTagSubstitution tagEntity) { try { - final IPlacementHandler placementHandler = PlacementHandlers.getHandler(null, BlockPos.ZERO, tagEntity.getReplacement().blockState()); + final IPlacementHandler placementHandler = PlacementHandlers.getHandler(null, BlockPos.ZERO, tagEntity.getReplacement().getBlockState()); if (placementHandler != this) { - final CompoundTag tileEntityData = tagEntity.getReplacement().serializedBE().orElse(null); + final CompoundTag tileEntityData = tagEntity.getReplacement().getBlockEntityTag(); Tuple updatedTETuple = null; - if (tileEntityData != null && !tileEntityData.isEmpty()) + if (!tileEntityData.isEmpty()) { updatedTETuple = new Tuple<>(blockEntityData.getA(), tileEntityData); } - return placementHandler.doesWorldStateMatchBlueprintState(worldState, tagEntity.getReplacement().blockState(), updatedTETuple, placementContext); + return placementHandler.doesWorldStateMatchBlueprintState(worldState, tagEntity.getReplacement().getBlockState(), updatedTETuple, placementContext); } else { @@ -148,10 +154,10 @@ public List getRequiredItems( { if (tileEntityData != null && BlockEntity.loadStatic(pos, blockState, tileEntityData, world.registryAccess()) instanceof BlockEntityTagSubstitution tagEntity) { - final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().blockState()); + final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().getBlockState()); if (placementHandler != this) { - return placementHandler.getRequiredItems(world, pos, tagEntity.getReplacement().blockState(), tagEntity.getReplacement().serializedBE().orElse(null), placementContext); + return placementHandler.getRequiredItems(world, pos, tagEntity.getReplacement().getBlockState(), tagEntity.getReplacement().getBlockEntityTag(), placementContext); } else { @@ -166,7 +172,7 @@ public List getRequiredItems( } } - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java index f76c0fa90d..b8d3f53172 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java @@ -6,8 +6,8 @@ import com.ldtteam.domumornamentum.entity.block.IMateriallyTexturedBlockEntity; import com.ldtteam.domumornamentum.entity.block.MateriallyTexturedBlockEntity; import com.ldtteam.domumornamentum.util.BlockUtils; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.InventoryUtils; @@ -15,7 +15,7 @@ import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; @@ -35,8 +35,7 @@ import java.util.Collections; import java.util.List; -import static com.ldtteam.domumornamentum.util.Constants.BLOCK_ENTITY_TEXTURE_DATA; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; public class DoBlockPlacementHandler implements IPlacementHandler @@ -92,9 +91,9 @@ public ActionProcessingResult handle( { try { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); - placementState.getBlock().setPlacedBy(world, pos, placementState, null, placementState.getBlock().getCloneItemStack(placementState, - new BlockHitResult(new Vec3(0, 0, 0), Direction.NORTH, pos, false), world, pos, null)); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); + placementState.getBlock().setPlacedBy(world, pos, placementState, null, + placementState.getBlock().getCloneItemStack(world, pos, placementState, true, null)); } catch (final Exception ex) { @@ -133,9 +132,9 @@ public static boolean compareBEData(final Tuple blockE if (blockEntityData.getA() instanceof final IMateriallyTexturedBlockEntity mtbe) { final String source; - if (blockEntityData.getB().contains(BLOCK_ENTITY_TEXTURE_DATA)) + if (blockEntityData.getB().contains("textureData")) { - source = BLOCK_ENTITY_TEXTURE_DATA; + source = "textureData"; } else if (blockEntityData.getB().contains("originalTextureData")) { @@ -145,7 +144,8 @@ else if (blockEntityData.getB().contains("originalTextureData")) { source = null; } - return source != null && mtbe.getTextureData().equals(MaterialTextureData.CODEC.decode(NbtOps.INSTANCE, blockEntityData.getB().get(source)).getOrThrow().getFirst()); + return source != null && mtbe.getTextureData() + .equals(MaterialTextureData.deserializeFromNBT(blockEntityData.getB().getCompoundOrEmpty(source))); } } return false; @@ -162,7 +162,7 @@ public List getRequiredItems( final List itemList = new ArrayList<>(); if (tileEntityData != null) { - BlockPos blockpos = new BlockPos(tileEntityData.getInt("x"), tileEntityData.getInt("y"), tileEntityData.getInt("z")); + BlockPos blockpos = new BlockPos(tileEntityData.getIntOr("x", 0), tileEntityData.getIntOr("y", 0), tileEntityData.getIntOr("z", 0)); final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, blockState, tileEntityData, world.registryAccess()); if (tileEntity == null) { @@ -191,4 +191,3 @@ public void handleRemoval( world.removeBlock(pos, false); } } - diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java index 55f3890ab0..fc479910f3 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java @@ -3,13 +3,12 @@ import com.ldtteam.domumornamentum.block.AbstractBlockDoor; import com.ldtteam.domumornamentum.block.IMateriallyTexturedBlock; import com.ldtteam.domumornamentum.util.BlockUtils; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.placement.IPlacementContext; -import com.ldtteam.structurize.placement.structure.IStructureHandler; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -23,7 +22,7 @@ import java.util.Collections; import java.util.List; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; import static com.ldtteam.structurize.placement.handlers.placement.DoBlockPlacementHandler.compareBEData; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; @@ -58,8 +57,8 @@ public ActionProcessingResult handle( { try { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); - handleTileEntityPlacement(tileEntityData, world, pos.above(), placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos.above(), placementContext.getRotationMirror().getRotationMirror()); } catch (final Exception ex) { @@ -81,7 +80,7 @@ public List getRequiredItems( final List itemList = new ArrayList<>(); if (tileEntityData != null && blockState.getValue(net.minecraft.world.level.block.DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) { - BlockPos blockpos = new BlockPos(tileEntityData.getInt("x"), tileEntityData.getInt("y"), tileEntityData.getInt("z")); + BlockPos blockpos = new BlockPos(tileEntityData.getIntOr("x", 0), tileEntityData.getIntOr("y", 0), tileEntityData.getIntOr("z", 0)); final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, blockState, tileEntityData, world.registryAccess()); if (tileEntity == null) { @@ -128,4 +127,3 @@ public boolean doesWorldStateMatchBlueprintState( return compareBEData(blockEntityData); } } - diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java index a8aaae9cfe..8381ff884b 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java @@ -6,9 +6,10 @@ import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.InventoryUtils; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index 41d97ae1f6..bd52a58191 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -1,20 +1,22 @@ package com.ldtteam.structurize.placement.handlers.placement; -import com.ldtteam.structurize.api.IRotatableBlockEntity; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.api.util.IRotatableBlockEntity; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.tag.ModTags; import com.ldtteam.structurize.util.BlockUtils; +import com.ldtteam.structurize.util.RotationMirror; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.tags.BlockTags; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.ItemEntity; @@ -33,7 +35,7 @@ import java.util.*; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; /** * Class containing all placement handler implementations. @@ -160,8 +162,8 @@ public static void add(IPlacementHandler handler) * @return the appropriate handler. */ public static IPlacementHandler getHandler(final Level world, - final BlockPos worldPos, - final BlockState newState) + final BlockPos worldPos, + final BlockState newState) { final Block block = newState.getBlock(); final IPlacementHandler cached = handlerCache.get(block); @@ -198,16 +200,16 @@ public static class FluidSubstitutionPlacementHandler implements IPlacementHandl @Override public boolean canHandle(Level world, BlockPos pos, BlockState blockState) { - return blockState.is(ModBlocks.blockFluidSubstitution); + return blockState.is(ModBlocks.blockFluidSubstitution.get()); } @Override public List getRequiredItems( - Level world, - BlockPos pos, - BlockState blockState, - @Nullable CompoundTag tileEntityData, - IPlacementContext placementContext) + Level world, + BlockPos pos, + BlockState blockState, + @Nullable CompoundTag tileEntityData, + IPlacementContext placementContext) { List items = new ArrayList<>(); @@ -230,16 +232,16 @@ public List getRequiredItems( @Override public void handleRemoval( - IStructureHandler handler, - Level world, - BlockPos pos, - CompoundTag tileEntityData) + IStructureHandler handler, + Level world, + BlockPos pos, + CompoundTag tileEntityData) { BlockState state = world.getBlockState(pos); // If there's no water there and there can be if (!(state.hasProperty(BlockStateProperties.WATERLOGGED) - && !state.getValue(BlockStateProperties.WATERLOGGED) - && BlockUtils.getFluidForDimension(world).getBlock() == Blocks.WATER)) + && !state.getValue(BlockStateProperties.WATERLOGGED) + && BlockUtils.getFluidForDimension(world).getBlock() == Blocks.WATER)) { handleRemoval(handler, world, pos); } @@ -295,11 +297,11 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); itemList.add(new ItemStack(Items.FLINT_AND_STEEL, 1)); @@ -308,11 +310,11 @@ public List getRequiredItems( @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return simplePlacement(world, pos, blockState, null, null); } @@ -338,13 +340,13 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); @@ -380,11 +382,11 @@ public List getRequiredItems( @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { @@ -409,7 +411,7 @@ public ActionProcessingResult handle( handleBlockPlacement(world, pos.below(), supportBlockState); } - if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData)) + if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData)) { return ActionProcessingResult.DENY; } @@ -438,22 +440,22 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return simplePlacement(world, pos, blockState, null, null); } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!placementContext.fancyPlacement()) { @@ -495,7 +497,7 @@ public ActionProcessingResult handle( { if (blockState.getValue(DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } return ActionProcessingResult.PASS; @@ -503,11 +505,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); if (blockState.getValue(DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) @@ -560,15 +562,15 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (blockState.getValue(BedBlock.PART) == BedPart.FOOT) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } return ActionProcessingResult.PASS; @@ -576,11 +578,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (blockState.getValue(BedBlock.PART) == BedPart.FOOT) { @@ -620,17 +622,17 @@ public ActionProcessingResult handle( { if (blockState.getValue(DoublePlantBlock.HALF).equals(DoubleBlockHalf.LOWER)) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } return ActionProcessingResult.PASS; } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); @@ -661,10 +663,10 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { return ActionProcessingResult.PASS; @@ -672,11 +674,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return new ArrayList<>(); } @@ -702,27 +704,27 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).getBlock() == Blocks.FLOWER_POT) { world.removeBlock(pos, false); } - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); if (world.getBlockState(pos).getBlock() != Blocks.FLOWER_POT) @@ -758,16 +760,16 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!world.isEmptyBlock(pos)) { final List entityList = - world.getEntitiesOfClass(Entity.class, new AABB(pos), entity -> !(entity instanceof LivingEntity || entity instanceof ItemEntity)); + world.getEntitiesOfClass(Entity.class, new AABB(pos), entity -> !(entity instanceof LivingEntity || entity instanceof ItemEntity)); if (!entityList.isEmpty()) { for (final Entity entity : entityList) @@ -783,11 +785,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return new ArrayList<>(); } @@ -819,16 +821,16 @@ public ActionProcessingResult handle( @Nullable final CompoundTag tileEntityData, final @NotNull IPlacementContext placementContext) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!placementContext.fancyPlacement()) { @@ -864,20 +866,20 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { world.removeBlock(pos, false); - handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); return ActionProcessingResult.PASS; } - if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData)) + if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData)) { return ActionProcessingResult.DENY; } @@ -887,13 +889,13 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; @@ -920,11 +922,11 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!handleBlockPlacement(world, pos, blockState)) { @@ -934,7 +936,7 @@ public ActionProcessingResult handle( try { // Try detecting inventory content. - ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState, world); + ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState); } catch (final Exception ex) { @@ -944,7 +946,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); } return ActionProcessingResult.SUCCESS; @@ -952,15 +954,15 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); - itemList.addAll(getItemsFromTileEntity(tileEntityData, blockState, world)); + itemList.addAll(getItemsFromTileEntity(tileEntityData, blockState)); itemList.removeIf(ItemStackUtils::isEmpty); @@ -1015,19 +1017,19 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); return ActionProcessingResult.PASS; } - if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData)) + if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData)) { return ActionProcessingResult.DENY; } @@ -1037,13 +1039,13 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); if (world.getBlockState(pos).equals(blockState)) { itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); @@ -1074,10 +1076,10 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { final BlockPos centerPos = placementContext.getCenterPos(); @@ -1093,13 +1095,13 @@ public ActionProcessingResult handle( return ActionProcessingResult.SUCCESS; } - if (blockState.getValue(PointedDripstoneBlock.THICKNESS) != DripstoneThickness.TIP && blockState.getValue(PointedDripstoneBlock.THICKNESS) != DripstoneThickness.TIP_MERGE) + if (blockState.getValue(PointedDripstoneBlock.THICKNESS) != SpeleothemThickness.TIP && blockState.getValue(PointedDripstoneBlock.THICKNESS) != SpeleothemThickness.TIP_MERGE) { return ActionProcessingResult.PASS; } final Direction dir = blockState.getValue(PointedDripstoneBlock.TIP_DIRECTION).getOpposite(); - if (blockState.getValue(PointedDripstoneBlock.THICKNESS) == DripstoneThickness.TIP_MERGE) + if (blockState.getValue(PointedDripstoneBlock.THICKNESS) == SpeleothemThickness.TIP_MERGE) { placeDripStoneInDir(dir.getOpposite(), blueprint, pos.subtract(centerPos).offset(blueprint.getPrimaryBlockOffset()), pos, blockState, world); placeDripStoneInDir(dir, blueprint, pos.subtract(centerPos).offset(blueprint.getPrimaryBlockOffset()), pos, blockState, world); @@ -1138,13 +1140,13 @@ private static void placeDripStoneInDir(final Direction dir, final Blueprint blu @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; @@ -1171,10 +1173,10 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { return ActionProcessingResult.PASS; @@ -1182,10 +1184,10 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { return Collections.emptyList(); @@ -1402,13 +1404,17 @@ public static void handleTileEntityPlacement(@Nullable final CompoundTag tileEnt { if (newTile instanceof final IRotatableBlockEntity rotatable) { - rotatable.rotateAndMirror(settings); + rotatable.rotate(settings.rotation()); + rotatable.mirror(settings.mirror()); } final BlockEntity worldBlockEntity = world.getBlockEntity(pos); if (worldBlockEntity != null) { - worldBlockEntity.loadWithComponents(newTile.saveWithFullMetadata(world.registryAccess()), world.registryAccess()); + worldBlockEntity.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, + world.registryAccess(), + newTile.saveWithFullMetadata(world.registryAccess()))); worldBlockEntity.setChanged(); } else @@ -1443,7 +1449,7 @@ public static List getRequiredItemsForState(final Level world, final * @param blockState the block. * @return the required list. */ - public static List getItemsFromTileEntity(final CompoundTag tileEntityData, final BlockState blockState, final Level provider) + public static List getItemsFromTileEntity(final CompoundTag tileEntityData, final BlockState blockState) { if (tileEntityData == null) { @@ -1451,7 +1457,7 @@ public static List getItemsFromTileEntity(final CompoundTag tileEntit } try { - return ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState, provider); + return ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState); } catch (final Exception ex) { diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java index 0bfb75ec6f..7056cebe9a 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java @@ -3,10 +3,12 @@ import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.core.BlockPos; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.entity.BlockEntity; import java.util.List; @@ -36,7 +38,7 @@ public abstract class AbstractStructureHandler implements IStructureHandler /** * The used settings for the placement. */ - private RotationMirror rotMir; + private PlacementSettings settings; /** * The minecraft world this struture is displayed in. @@ -54,13 +56,13 @@ public abstract class AbstractStructureHandler implements IStructureHandler * @param world the world it gets. * @param worldPos the position the anchor of the structure got placed. * @param blueprintFuture the name of the structure. - * @param rotMir the placement settings. + * @param settings the placement settings. */ - public AbstractStructureHandler(final Level world, final BlockPos worldPos, final Future blueprintFuture, final RotationMirror rotMir) + public AbstractStructureHandler(final Level world, final BlockPos worldPos, final Future blueprintFuture, final PlacementSettings settings) { this.world = world; this.worldPos = worldPos; - this.rotMir = rotMir; + this.settings = settings; this.blueprintFuture = blueprintFuture; } @@ -69,13 +71,13 @@ public AbstractStructureHandler(final Level world, final BlockPos worldPos, fina * @param world the world. * @param pos the position. * @param blueprint the blueprint. - * @param rotMir the placement settings. + * @param settings the placement settings. */ - public AbstractStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final RotationMirror rotMir) + public AbstractStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final PlacementSettings settings) { this.world = world; this.worldPos = pos; - this.rotMir = rotMir; + this.settings = settings; this.blueprint = blueprint; } @@ -119,7 +121,7 @@ public Blueprint getBluePrint() try { blueprint = blueprintFuture.get(); - blueprint.setRotationMirror(rotMir, world); + blueprint.setRotationMirror(settings.getRotationMirror(), world); } catch (InterruptedException | ExecutionException e) { @@ -144,13 +146,19 @@ public String getMd5() @Override public BlockPos getCenterPos() { - return this.worldPos; + return worldPos; } @Override - public RotationMirror getRotationMirror() + public PlacementSettings getSettings() { - return this.rotMir; + return this.settings; + } + + @Override + public PlacementSettings getRotationMirror() + { + return this.settings; } @Override diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java index fd2f9e8635..d366773ded 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java @@ -3,7 +3,7 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.util.BlockUtils; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.item.ItemStack; import net.minecraft.core.BlockPos; @@ -30,12 +30,12 @@ public class CreativeStructureHandler extends AbstractStructureHandler * @param world the world it gets. * @param pos the position the anchor of the structure got placed. * @param blueprint the blueprint. - * @param rotMir the placement settings. + * @param settings the placement settings. * @param fancyPlacement if placement is fancy or complete. */ - public CreativeStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final RotationMirror rotMir, final boolean fancyPlacement) + public CreativeStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final PlacementSettings settings, final boolean fancyPlacement) { - super(world, pos, blueprint, rotMir); + super(world, pos, blueprint, settings); this.fancyPlacement = fancyPlacement; } @@ -44,12 +44,12 @@ public CreativeStructureHandler(final Level world, final BlockPos pos, final Blu * @param world the world it gets. * @param pos the position the anchor of the structure got placed. * @param blueprint the blueprint. - * @param rotMir the placement settings. + * @param settings the placement settings. * @param fancyPlacement if placement is fancy or complete. */ - public CreativeStructureHandler(final Level world, final BlockPos pos, final Future blueprint, final RotationMirror rotMir, final boolean fancyPlacement) + public CreativeStructureHandler(final Level world, final BlockPos pos, final Future blueprint, final PlacementSettings settings, final boolean fancyPlacement) { - super(world, pos, blueprint, rotMir); + super(world, pos, blueprint, settings); this.fancyPlacement = fancyPlacement; } @@ -132,6 +132,12 @@ public void prePlacementLogic(final BlockPos worldPos, final BlockState blockSta // Do nothing } + @Override + public BlockState getSolidBlockForPos(final BlockPos worldPos) + { + return BlockUtils.getSubstitutionBlockAtWorld(getWorld(), worldPos, null); + } + @Override public BlockState getSolidBlockForPos(final BlockPos worldPos, final Function virtualBlocks) { diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java index c7f59567a8..31facb1c15 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java @@ -1,10 +1,11 @@ package com.ldtteam.structurize.placement.structure; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.util.InventoryUtils; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.item.ItemStack; import net.minecraft.core.BlockPos; @@ -12,6 +13,7 @@ import net.neoforged.neoforge.items.IItemHandler; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.function.Function; /** * A handler for structures. @@ -59,6 +61,12 @@ default boolean isCorrectMD5(final String otherMD5) */ Level getWorld(); + /** + * Getter for the placement settings. + * @return the settings object. + */ + PlacementSettings getSettings(); + /** * Get the inventory of the handler. * @return the IItemhandler (may be null!). @@ -194,6 +202,24 @@ default BlockPos getStructurePosFromWorld(final BlockPos worldPos) */ void prePlacementLogic(final BlockPos worldPos, final BlockState blockState, final List requiredItems); + /** + * Get the right solid block for the substitution block. + * @param worldPos the world pos. + * @return the right block (classically biome dependent). + */ + @Deprecated(forRemoval = true, since = "1.18.2") + BlockState getSolidBlockForPos(BlockPos worldPos); + + /** + * Get the solid worldgen block for given pos while using data from handler. + * + * @param worldPos the world pos. + * @param virtualBlocks if null use level instead for getting surrounding block states, fnc may should return null if virtual + * block is not available + * @return the solid worldgen block (classically biome dependent). + */ + BlockState getSolidBlockForPos(BlockPos worldPos, @Nullable Function virtualBlocks); + /** * Check if the handler is ready. * @return true if so. diff --git a/src/main/java/com/ldtteam/structurize/proxy/ClientProxy.java b/src/main/java/com/ldtteam/structurize/proxy/ClientProxy.java new file mode 100644 index 0000000000..ea9f518d21 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/proxy/ClientProxy.java @@ -0,0 +1,42 @@ +package com.ldtteam.structurize.proxy; + +import com.ldtteam.structurize.client.gui.WindowExtendedBuildTool; +import com.ldtteam.structurize.client.gui.WindowShapeTool; +import com.ldtteam.structurize.storage.rendering.RenderingCache; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import org.jetbrains.annotations.Nullable; + +/** + * Client side proxy. + */ +public class ClientProxy implements IProxy +{ + @Override + @SuppressWarnings("resource") + public void openBuildToolWindow(@Nullable final BlockPos pos, final int groundstyle) + { + if (Minecraft.getInstance().gui.screen() != null) + { + return; + } + + @Nullable + final WindowExtendedBuildTool window = new WindowExtendedBuildTool(pos, groundstyle, null, WindowExtendedBuildTool.BLOCK_BLUEPRINT_REQUIREMENT); + window.open(); + } + + @Override + public void openShapeToolWindow(@Nullable final BlockPos pos) + { + /*if (pos == null && OldSettings.instance.getActiveStructure() == null) + { + todo shapetool + return; + }*/ + + @Nullable + final WindowShapeTool window = new WindowShapeTool(pos); + window.open(); + } +} diff --git a/src/main/java/com/ldtteam/structurize/proxy/IProxy.java b/src/main/java/com/ldtteam/structurize/proxy/IProxy.java new file mode 100644 index 0000000000..44a141ce50 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/proxy/IProxy.java @@ -0,0 +1,29 @@ +package com.ldtteam.structurize.proxy; + +import net.minecraft.core.BlockPos; + +/** + * Basic proxy interface. + */ +public interface IProxy +{ + /** + * Opens a build tool window. + * + * @param pos coordinates. + * @param groundstyle one of the GROUNDSTYLE_ values. + */ + default void openBuildToolWindow(final BlockPos pos, final int groundstyle) + { + + } + + /** + * Opens a shape tool window. + * + * @param pos coordinates. + */ + default void openShapeToolWindow(final BlockPos pos) + { + } +} diff --git a/src/main/java/com/ldtteam/structurize/proxy/ServerProxy.java b/src/main/java/com/ldtteam/structurize/proxy/ServerProxy.java new file mode 100644 index 0000000000..79053bb86e --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/proxy/ServerProxy.java @@ -0,0 +1,9 @@ +package com.ldtteam.structurize.proxy; + +/** + * Proxy to the server. + */ +public class ServerProxy implements IProxy +{ + +} diff --git a/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java b/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java index 011b138592..be5392cdfe 100644 --- a/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java +++ b/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java @@ -2,9 +2,10 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.interfaces.ISpecialCreativeHandlerAnchorBlock; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.management.Manager; @@ -16,6 +17,8 @@ import com.ldtteam.structurize.placement.structure.CreativeStructureHandler; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.IOPool; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.fml.ModList; @@ -29,7 +32,7 @@ import java.util.ArrayList; import java.util.List; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; /** * Class where blueprint placement is handled. @@ -44,11 +47,11 @@ public static void handlePlacement(final BuildToolPlacementMessage message) { if (!StructurePacks.hasPack(message.structurePackId)) { - new ClientBlueprintRequestMessage(message).sendToPlayer(message.player); + Network.getNetwork().sendToPlayer(new ClientBlueprintRequestMessage(message), message.player); } else { - ServerFutureProcessor.queueBlueprint(new ServerFutureProcessor.BlueprintProcessingData(StructurePacks.getBlueprintFuture(message.structurePackId, message.blueprintPath, message.world.registryAccess()), + ServerFutureProcessor.queueBlueprint(new ServerFutureProcessor.BlueprintProcessingData(StructurePacks.getBlueprintFuture(message.structurePackId, message.blueprintPath), message.world, (blueprint) -> process(blueprint, message))); } } @@ -77,28 +80,34 @@ private static void process(final @Nullable Blueprint blueprint, final @NotNull message.world, message.player, message.pos, - message.rotationMirror); + new PlacementSettings(message.mirror, message.rotation)); } return; } Utils.playSuccessSound(message.player); final BlockState anchor = blueprint.getBlockState(blueprint.getPrimaryBlockOffset()); - blueprint.setRotationMirror(message.rotationMirror, message.world); + blueprint.setRotationMirror(RotationMirror.of(message.rotation, message.mirror), message.world); final IStructureHandler structure; - final boolean fancyPlacement = message.type == BuildToolPlacementMessage.HandlerType.Pretty; - if (anchor.getBlock() instanceof final ISpecialCreativeHandlerAnchorBlock specialAnchor) + if (anchor.getBlock() instanceof ISpecialCreativeHandlerAnchorBlock) { - if (!specialAnchor.setup(message.player, message.world, message.pos, blueprint, message.rotationMirror, fancyPlacement, message.structurePackId, message.blueprintPath)) - { - return; - } - structure = specialAnchor.getStructureHandler(message.world, message.pos, blueprint, message.rotationMirror, fancyPlacement); + if (!((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).setup(message.player, message.world, message.pos, blueprint, new PlacementSettings(message.mirror, message.rotation), + message.type == BuildToolPlacementMessage.HandlerType.Pretty, message.structurePackId, message.blueprintPath)) + { + return; + } + structure = + ((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).getStructureHandler(message.world, message.pos, blueprint, new PlacementSettings(message.mirror, message.rotation), + message.type == BuildToolPlacementMessage.HandlerType.Pretty); } else { - structure = new CreativeStructureHandler(message.world, message.pos, blueprint, message.rotationMirror, fancyPlacement); + structure = new CreativeStructureHandler(message.world, + message.pos, + blueprint, + new PlacementSettings(message.mirror, message.rotation), + message.type == BuildToolPlacementMessage.HandlerType.Pretty); } final StructurePlacer instantPlacer = new StructurePlacer(structure); @@ -164,7 +173,7 @@ public static void handlePlacement(final BlueprintSyncMessage blueprintSyncMessa Log.getLogger().error("Failed to save blueprint file for client blueprint: " + blueprintSyncMessage.blueprintPath, e); } - return StructurePacks.getBlueprint(packId, blueprintPath, player.level().registryAccess()); + return StructurePacks.getBlueprint(packId, blueprintPath); }), player.level(), blueprint -> process(blueprint, new BuildToolPlacementMessage(blueprintSyncMessage, player, player.level())))); } } diff --git a/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java b/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java index 98bbccdba4..96587b2158 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java +++ b/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java @@ -1,9 +1,10 @@ package com.ldtteam.structurize.storage; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.bus.api.SubscribeEvent; import org.jetbrains.annotations.NotNull; + import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.ExecutionException; diff --git a/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java b/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java index 32e2c7d61d..68ecea2a15 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java +++ b/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java @@ -2,10 +2,11 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.network.messages.NotifyServerAboutStructurePacksMessage; import com.ldtteam.structurize.network.messages.SyncSettingsToServer; import com.ldtteam.structurize.storage.rendering.RenderingCache; @@ -14,24 +15,25 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import net.minecraft.client.Minecraft; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; +import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModList; -import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.neoforgespi.language.IModInfo; + import java.io.*; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.*; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; /** * Client side structure pack discovery. @@ -63,7 +65,14 @@ public static void onClientLoading() final List modList = new ArrayList<>(); for (IModInfo mod : ModList.get().getMods()) { - modPaths.add(mod.getOwningFile().getFile().findResource(BLUEPRINT_FOLDER, mod.getModId())); + // NeoForge mods are normally jar-backed. Resolving a child path + // from getFilePath() treats the jar itself as a directory and + // leaves every built-in pack undiscoverable. JarContents exposes + // the resource through a URI that works for jar and dev files. + modPaths.add(mod.getOwningFile().getFile().getContents() + .findFile(BLUEPRINT_FOLDER + "/" + mod.getModId()) + .map(uri -> Paths.get(uri)) + .orElse(null)); modList.add(mod.getModId()); } @@ -78,13 +87,19 @@ public static void onClientLoading() IOPool.execute(() -> { // This loads from the jar - for (final Path modPath : modPaths) + for (int index = 0; index < modPaths.size(); index++) { + final Path modPath = modPaths.get(index); + if (modPath == null || !Files.isDirectory(modPath)) + { + continue; + } + final String owner = modList.get(index); try { try (final Stream paths = Files.list(modPath)) { - paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, modPath.toString().split("/")[1])); + paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, owner)); } } catch (IOException e) @@ -144,7 +159,7 @@ public static void onWorldTick(final ClientTickEvent.Pre event) { if (Minecraft.getInstance().level != null && loadingState == ClientLoadingState.FINISHED_LOADING) { - if (Minecraft.getInstance().isSingleplayer()) + if (Minecraft.getInstance().getSingleplayerServer() != null) { loadingState = ClientLoadingState.FINISHED_SYNCING; StructurePacks.setFinishedLoading(); @@ -153,7 +168,7 @@ public static void onWorldTick(final ClientTickEvent.Pre event) } loadingState = ClientLoadingState.SYNCING; - new NotifyServerAboutStructurePacksMessage(StructurePacks.getPackMetas()).sendToServer(); + Network.getNetwork().sendToServer(new NotifyServerAboutStructurePacksMessage(StructurePacks.getPackMetas())); } else if (Minecraft.getInstance().level == null && (loadingState == ClientLoadingState.SYNCING || loadingState == ClientLoadingState.FINISHED_SYNCING)) { @@ -172,7 +187,7 @@ else if (Minecraft.getInstance().level == null && (loadingState == ClientLoading */ public static void onServerSyncAttempt(final Map serverStructurePacks) { - new SyncSettingsToServer().sendToServer(); + Network.getNetwork().sendToServer(new SyncSettingsToServer()); if (serverStructurePacks.isEmpty()) { @@ -183,7 +198,7 @@ public static void onServerSyncAttempt(final Map serverStructure return; } - if (serverStructurePacks.containsKey(Minecraft.getInstance().player.getGameProfile().getName())) + if (serverStructurePacks.containsKey(Minecraft.getInstance().player.getGameProfile().name())) { Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.pack.equaluser.error")); } @@ -344,7 +359,7 @@ public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) throws IOEx * @param compound compound to store. * @param fileName milli seconds for fileName. */ - public static void handleSaveScanMessage(final CompoundTag compound, final String fileName, final HolderLookup.Provider provider) + public static void handleSaveScanMessage(final CompoundTag compound, final String fileName) { final String packName = Utils.getSafePackName(Minecraft.getInstance().getUser().getName()); StructurePacks.switchSelectedPack(StructurePacks.getStructurePack(Utils.getSafePackName(Minecraft.getInstance().getUser().getName()))); @@ -352,8 +367,8 @@ public static void handleSaveScanMessage(final CompoundTag compound, final Strin StructurePacks.storeBlueprint(packName, compound, Minecraft.getInstance().gameDirectory.toPath() .resolve(BLUEPRINT_FOLDER) .resolve(packName.toLowerCase(Locale.US)) - .resolve(SCANS_FOLDER).resolve(fileName), provider)); + .resolve(SCANS_FOLDER).resolve(fileName))); RenderingCache.getOrCreateBlueprintPreviewData("blueprint").setPos(null); - Minecraft.getInstance().player.displayClientMessage(Component.translatable("Scan successfully saved as %s", fileName), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("Scan successfully saved as %s", fileName)); } } diff --git a/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java b/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java index 768454aa4d..3e6895330c 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java +++ b/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.storage; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; @@ -31,10 +31,10 @@ public interface ISurvivalBlueprintHandler * @param level the world. * @param player the player trying to place it. * @param pos the position they're trying to place it at. - * @param rotMir the placement settings. + * @param placementSettings the placement settings. * @return true if so. */ - boolean canHandle(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final RotationMirror rotMir); + boolean canHandle(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final PlacementSettings placementSettings); /** * Handle the placement. @@ -45,7 +45,7 @@ public interface ISurvivalBlueprintHandler * @param level the world. * @param player the player placing it. * @param pos the position they're placing it at. - * @param rotMir the placement settings. + * @param placementSettings the placement settings. */ void handle( final Blueprint blueprint, @@ -54,5 +54,5 @@ void handle( final Level level, final Player player, final BlockPos pos, - final RotationMirror rotMir); + final PlacementSettings placementSettings); } diff --git a/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java b/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java index 120ea3ea3f..de3c2ff004 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java +++ b/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java @@ -2,9 +2,10 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import net.minecraft.world.level.Level; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.tick.LevelTickEvent; +import net.neoforged.bus.api.SubscribeEvent; import org.jetbrains.annotations.NotNull; + import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -62,7 +63,8 @@ public static void queueBlueprintData(@NotNull final BlueprintDataProcessingData @SubscribeEvent public static void onWorldTick(final LevelTickEvent.Post event) { - if (!blueprintConsumerQueue.isEmpty() && blueprintConsumerQueue.peek().level == event.getLevel() && blueprintConsumerQueue.peek().blueprintFuture.isDone()) + final Level level = event.getLevel(); + if (!blueprintConsumerQueue.isEmpty() && blueprintConsumerQueue.peek().level == level && blueprintConsumerQueue.peek().blueprintFuture.isDone()) { final BlueprintProcessingData data = blueprintConsumerQueue.poll(); try @@ -75,7 +77,7 @@ public static void onWorldTick(final LevelTickEvent.Post event) } } - if (!blueprintDataConsumerQueue.isEmpty() && blueprintDataConsumerQueue.peek().level == event.getLevel() && blueprintDataConsumerQueue.peek().blueprintDataFuture.isDone()) + if (!blueprintDataConsumerQueue.isEmpty() && blueprintDataConsumerQueue.peek().level == level && blueprintDataConsumerQueue.peek().blueprintDataFuture.isDone()) { final BlueprintDataProcessingData data = blueprintDataConsumerQueue.poll(); try @@ -88,7 +90,7 @@ public static void onWorldTick(final LevelTickEvent.Post event) } } - if (!blueprintListConsumerQueue.isEmpty() && blueprintListConsumerQueue.peek().level == event.getLevel() && blueprintListConsumerQueue.peek().blueprintFuture.isDone()) + if (!blueprintListConsumerQueue.isEmpty() && blueprintListConsumerQueue.peek().level == level && blueprintListConsumerQueue.peek().blueprintFuture.isDone()) { final BlueprintListProcessingData data = blueprintListConsumerQueue.poll(); try diff --git a/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java b/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java index 64bc61b4b8..10643e7663 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java +++ b/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.storage; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.network.messages.NotifyClientAboutStructurePacksMessage; import com.ldtteam.structurize.network.messages.TransferStructurePackToClient; import com.ldtteam.structurize.util.IOPool; @@ -8,10 +9,11 @@ import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.event.tick.ServerTickEvent; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModList; -import net.neoforged.neoforge.event.tick.ServerTickEvent; import net.neoforged.neoforgespi.language.IModInfo; + import org.jetbrains.annotations.NotNull; import java.io.File; @@ -24,7 +26,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; /** * Here we load the structure packs on the server side. @@ -67,7 +69,12 @@ public static void onServerStarting() final List modList = new ArrayList<>(); for (IModInfo mod : ModList.get().getMods()) { - modPaths.add(mod.getOwningFile().getFile().findResource(BLUEPRINT_FOLDER, mod.getModId())); + // Resolve resources through NeoForge's JarContents so packs in a + // jar-backed mod are visible in production as well as dev runs. + modPaths.add(mod.getOwningFile().getFile().getContents() + .findFile(BLUEPRINT_FOLDER + "/" + mod.getModId()) + .map(uri -> Paths.get(uri)) + .orElse(null)); modList.add(mod.getModId()); } @@ -78,13 +85,19 @@ public static void onServerStarting() try { // This loads from the jar - for (final Path modPath : modPaths) + for (int index = 0; index < modPaths.size(); index++) { + final Path modPath = modPaths.get(index); + if (modPath == null || !Files.isDirectory(modPath)) + { + continue; + } + final String owner = modList.get(index); try { try (final Stream paths = Files.list(modPath)) { - paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, modPath.toString().split("/")[1])); + paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, owner)); } } catch (IOException e) @@ -162,7 +175,7 @@ public static void onClientSyncAttempt(final Map clientStructure { if (loadingState == ServerLoadingState.UNINITIALIZED) { - new NotifyClientAboutStructurePacksMessage(List.of()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new NotifyClientAboutStructurePacksMessage(List.of()), player); // Noop Single Player, Nothing to do here. return; } @@ -197,11 +210,14 @@ public static void onWorldTick(final ServerTickEvent.Post event) if (!messageSendTasks.isEmpty()) { final PackagedPack packData = messageSendTasks.poll(); - final ServerPlayer player = event.getServer().getPlayerList().getPlayer(packData.player); - // If the player logged off, we can just skip. - if (player != null) + if (packData != null) { - new TransferStructurePackToClient(packData.structurePack, packData.buf, packData.eol).sendToPlayer(player); + final ServerPlayer player = event.getServer().getPlayerList().getPlayer(packData.player); + // If the player logged off, we can just skip. + if (player != null) + { + Network.getNetwork().sendToPlayer(new TransferStructurePackToClient(packData.structurePack, packData.buf, packData.eol), player); + } } } } @@ -212,7 +228,7 @@ public static void onWorldTick(final ServerTickEvent.Post event) */ private static void handleClientUpdate(final Map clientStructurePacks, final ServerPlayer player) { - new NotifyClientAboutStructurePacksMessage(StructurePacks.getPackMetas()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new NotifyClientAboutStructurePacksMessage(StructurePacks.getPackMetas()), player); final UUID uuid = player.getUUID(); final Map missingPacks = new HashMap<>(); diff --git a/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java b/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java index 97a417a4ec..3fc698aa84 100644 --- a/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java +++ b/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java @@ -2,19 +2,21 @@ import com.google.gson.internal.Streams; import com.google.gson.stream.JsonReader; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; import com.ldtteam.structurize.util.IOPool; import com.ldtteam.structurize.util.ManualBarrier; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtAccounter; import org.jetbrains.annotations.Nullable; -import java.io.*; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -143,9 +145,9 @@ public static StructurePackMeta removePack(final String name) * @param subPath the path of the specific blueprint in the pack. * @return the blueprint future (might contain null). */ - public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath) { - return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath), IOPool.getExecutor()); } /** @@ -176,9 +178,9 @@ public static CompletableFuture findBlueprintFuture(final String structure * @param subPath the path of the set of blueprints (usually a folder). * @return the blueprints list (might be empty). */ - public static CompletableFuture> getBlueprintsFuture(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static CompletableFuture> getBlueprintsFuture(final String structurePackId, final String subPath) { - return CompletableFuture.supplyAsync(() -> getBlueprints(structurePackId, subPath, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprints(structurePackId, subPath), IOPool.getExecutor()); } /** @@ -198,9 +200,9 @@ public static CompletableFuture> getCategoriesFuture(final String * @param path the path to search for. * @return the blueprint. */ - public static CompletableFuture getBlueprintFuture(final String packName, final Path path, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String packName, final Path path) { - return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path), IOPool.getExecutor()); } /** @@ -208,9 +210,9 @@ public static CompletableFuture getBlueprintFuture(final String packN * @param blueprintPredicate the predicate to define the blueprint we're looking for. * @return the blueprint future. */ - public static CompletableFuture findBlueprintFuture(final String structurePackId, final Predicate blueprintPredicate, final HolderLookup.Provider provider) + public static CompletableFuture findBlueprintFuture(final String structurePackId, final Predicate blueprintPredicate) { - return CompletableFuture.supplyAsync(() -> findBlueprint(structurePackId, blueprintPredicate, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> findBlueprint(structurePackId, blueprintPredicate), IOPool.getExecutor()); } /** @@ -220,9 +222,9 @@ public static CompletableFuture findBlueprintFuture(final String stru * @param suppressError log exception or not. * @return the blueprint future (might contain null). */ - public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath, final boolean suppressError, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath, final boolean suppressError) { - return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath, suppressError, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath, suppressError), IOPool.getExecutor()); } @@ -233,9 +235,9 @@ public static CompletableFuture getBlueprintFuture(final String struc * @param suppressError log exception or not. * @return the blueprint. */ - public static CompletableFuture getBlueprintFuture(final String packName, final Path path, final boolean suppressError, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String packName, final Path path, final boolean suppressError) { - return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path, suppressError, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path, suppressError), IOPool.getExecutor()); } // ------------------------- Synchronous Calls ------------------------- // @@ -247,9 +249,9 @@ public static CompletableFuture getBlueprintFuture(final String packN * @return the blueprint or null. */ @Nullable - public static Blueprint getBlueprint(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String structurePackId, final String subPath) { - return getBlueprint(structurePackId, subPath, false, provider); + return getBlueprint(structurePackId, subPath, false); } /** @@ -258,9 +260,9 @@ public static Blueprint getBlueprint(final String structurePackId, final String * @param path the path to search for. * @return the blueprint. */ - public static Blueprint getBlueprint(final String pack, final Path path, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String pack, final Path path) { - return getBlueprint(pack, path, false, provider); + return getBlueprint(pack, path, false); } /** @@ -326,7 +328,7 @@ public static Optional findBlueprint(final Path subPath, final String name * @param blueprintPredicate matches the blueprint. * @return the blueprint or null. */ - public static Blueprint findBlueprint(final String structurePackId, final Predicate blueprintPredicate, final HolderLookup.Provider provider) + public static Blueprint findBlueprint(final String structurePackId, final Predicate blueprintPredicate) { if (!waitUntilFinishedLoading()) { @@ -339,7 +341,7 @@ public static Blueprint findBlueprint(final String structurePackId, final Predic return null; } - return findBlueprint(packMeta.getName(), packMeta.getPath(), blueprintPredicate, provider); + return findBlueprint(packMeta.getName(), packMeta.getPath(), blueprintPredicate); } /** @@ -350,7 +352,7 @@ public static Blueprint findBlueprint(final String structurePackId, final Predic * @param blueprintPredicate matches the blueprint. * @return the path of the file or null. */ - public static Blueprint findBlueprint(final String pack, final Path subPath, final Predicate blueprintPredicate, final HolderLookup.Provider provider) + public static Blueprint findBlueprint(final String pack, final Path subPath, final Predicate blueprintPredicate) { if (!waitUntilFinishedLoading()) { @@ -364,7 +366,7 @@ public static Blueprint findBlueprint(final String pack, final Path subPath, fin return paths.map(file -> { if (!Files.isDirectory(file) && file.toString().endsWith("blueprint")) { - final Blueprint blueprint = getBlueprint(pack, file, provider); + final Blueprint blueprint = getBlueprint(pack, file); if (blueprintPredicate.test(blueprint)) { blueprint.setFileName(file.getFileName().toString().replace(".blueprint", "")); @@ -374,7 +376,7 @@ public static Blueprint findBlueprint(final String pack, final Path subPath, fin } else if (Files.isDirectory(file)) { - return findBlueprint(pack, file, blueprintPredicate, provider); + return findBlueprint(pack, file, blueprintPredicate); } return null; }).filter(Objects::nonNull).findFirst().orElse(null); @@ -395,7 +397,7 @@ else if (Files.isDirectory(file)) * @return the blueprint or null. */ @Nullable - public static Blueprint getBlueprint(final String structurePackId, final String subPath, final boolean suppressError, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String structurePackId, final String subPath, final boolean suppressError) { if (!waitUntilFinishedLoading()) { @@ -410,7 +412,7 @@ public static Blueprint getBlueprint(final String structurePackId, final String //todo, here similarly as in the other places we could query a remote server for this if we don't have it locally. - return getBlueprint(structurePackId, packMeta.getPath().resolve(packMeta.getNormalizedSubPath(subPath)), suppressError, provider); + return getBlueprint(structurePackId, packMeta.getPath().resolve(packMeta.getNormalizedSubPath(subPath)), suppressError); } /** @@ -420,12 +422,12 @@ public static Blueprint getBlueprint(final String structurePackId, final String * @param suppressError log exception or not. * @return the blueprint. */ - public static Blueprint getBlueprint(final String pack, final Path path, final boolean suppressError, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String pack, final Path path, final boolean suppressError) { try { final CompoundTag nbt = NbtIo.readCompressed(new ByteArrayInputStream(Files.readAllBytes(path)), NbtAccounter.unlimitedHeap()); - final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt, provider); + final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt); if (blueprint == null) return null; blueprint.setFileName(path.getFileName().toString().replace(".blueprint", "")); @@ -481,7 +483,7 @@ public static byte[] getBlueprintData(final String structurePackId, final String * @param subPath the folder containing the blueprints. * @return the list of blueprints or empty */ - public static List getBlueprints(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static List getBlueprints(final String structurePackId, final String subPath) { if (!waitUntilFinishedLoading()) { @@ -508,7 +510,7 @@ public static List getBlueprints(final String structurePackId, final try { final CompoundTag nbt = NbtIo.readCompressed(new ByteArrayInputStream(Files.readAllBytes(file)), NbtAccounter.unlimitedHeap()); - final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt, provider); + final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt); if (blueprint != null) { blueprint.setFileName(file.getFileName().toString().replace(".blueprint", "")); @@ -693,7 +695,7 @@ public static void discoverPackAtPath(final Path element, final boolean immutabl * @param compoundTag compound to store. * @param path path to store it at. */ - public static Future storeBlueprint(final String packName, final CompoundTag compoundTag, final Path path, final HolderLookup.Provider provider) + public static Future storeBlueprint(final String packName, final CompoundTag compoundTag, final Path path) { return IOPool.submit(() -> { @@ -707,7 +709,7 @@ public static Future storeBlueprint(final String packName, final Comp Log.getLogger().warn("Exception while trying to scan.", e); return null; } - return StructurePacks.getBlueprint(packName, path, provider); + return StructurePacks.getBlueprint(packName, path); }); } diff --git a/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java b/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java index 495e92e2b1..7e1542aac8 100644 --- a/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java +++ b/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java @@ -2,7 +2,7 @@ import com.google.common.collect.ImmutableList; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Player; @@ -60,12 +60,12 @@ public static void registerHandler(final ISurvivalBlueprintHandler handler) * Get all handlers that can take over the placement operation. * @return */ - public static List getMatchingHandlers(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final RotationMirror rotMir) + public static List getMatchingHandlers(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final PlacementSettings settings) { final List matchingHandlers = new ArrayList<>(); for (final ISurvivalBlueprintHandler handler : handlers.values()) { - if (handler.canHandle(blueprint, level, player, pos, rotMir)) + if (handler.canHandle(blueprint, level, player, pos, settings)) { matchingHandlers.add(handler); } diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java b/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java index 4fe8f87af7..2245add6c7 100644 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java +++ b/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java @@ -3,22 +3,16 @@ import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.network.messages.SyncPreviewCacheToClient; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** - * Rendering cache for boxes, blueprints, etc. TODO: + * Rendering cache for boxes, blueprints, etc. */ public class RenderingCache { - /** - * Boxes to render. - */ - public static Map boxRenderingCache = new HashMap<>(); - /** * Blueprints to render. */ @@ -34,16 +28,6 @@ public static boolean hasBlueprint(final String key) return blueprintRenderingCache.containsKey(key); } - /** - * Get the preview data for a box. - * @param key the key of the box. - * @return the preview data. - */ - public static BoxPreviewData getBoxPreviewData(final String key) - { - return boxRenderingCache.get(key); - } - /** * Get the preview data for a blueprint. * @param key the key of the blueprint. @@ -62,25 +46,6 @@ public static Collection getBlueprintsToRender() return blueprintRenderingCache.values(); } - /** - * Get a list of all blueprints to render. - * @return the preview data. - */ - public static Collection getBoxesToRender() - { - return boxRenderingCache.values(); - } - - /** - * Queue a box to be rendered. - * @param key the key to queue it under. - * @param boxPreviewData the preview data. - */ - public static void queue(final String key, final BoxPreviewData boxPreviewData) - { - boxRenderingCache.put(key, boxPreviewData); - } - /** * Queue a blueprint to be rendered. * @param key the key to queue it under. @@ -91,16 +56,6 @@ public static void queue(final String key, final BlueprintPreviewData boxPreview blueprintRenderingCache.put(key, boxPreviewData); } - /** - * Remove an item from the cache. - * @param key the key of the item to be removed. - * @return the removed data. - */ - public static BoxPreviewData removeBox(final String key) - { - return boxRenderingCache.remove(key); - } - /** * Remove an item from the cache. * @param key the key of the item to be removed. @@ -124,7 +79,7 @@ public static BlueprintPreviewData getOrCreateBlueprintPreviewData(final String /** * @return true when should use light level from {@link #getOurLightLevel()} */ - @Deprecated(forRemoval = true, since = "1.21.1") + @Deprecated public static boolean forceLightLevel() { return Structurize.getConfig().getClient().rendererLightLevel.get() >= 0; @@ -133,7 +88,7 @@ public static boolean forceLightLevel() /** * @return static light level */ - @Deprecated(forRemoval = true, since = "1.21.1") + @Deprecated public static int getOurLightLevel() { return Structurize.getConfig().getClient().rendererLightLevel.get(); @@ -145,7 +100,6 @@ public static int getOurLightLevel() public static void clear() { blueprintRenderingCache.clear(); - boxRenderingCache.clear(); } /** diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java b/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java index f8db486ab8..25c3b52c02 100644 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java +++ b/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java @@ -1,13 +1,14 @@ package com.ldtteam.structurize.storage.rendering; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.network.messages.SyncPreviewCacheToClient; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.bus.api.SubscribeEvent; import java.util.UUID; /** @@ -23,7 +24,7 @@ public class ServerPreviewDistributor @SubscribeEvent public static void onLogout(final PlayerEvent.PlayerLoggedOutEvent event) { - if (event.getEntity().level().isClientSide) + if (event.getEntity().level().isClientSide()) { RenderingCache.clear(); return; @@ -37,14 +38,14 @@ public static void onLogout(final PlayerEvent.PlayerLoggedOutEvent event) */ public static void distribute(final BlueprintPreviewData renderingCache, final ServerPlayer sourcePlayer) { - for (final ServerPlayer player : sourcePlayer.getServer().getLevel(sourcePlayer.level().dimension()).players()) + for (final ServerPlayer player : sourcePlayer.level().getServer().getLevel(sourcePlayer.level().dimension()).players()) { if ((player.blockPosition().distSqr(renderingCache.getPos()) < 128 * 128 || renderingCache.getPos().equals(BlockPos.ZERO)) && // within sensible distance !player.getUUID().equals(sourcePlayer.getUUID()) && // dont send to source player.isAlive() && // dont send to dead registeredPlayers.getBoolean(player.getUUID())) // only those who want to see previews { - new SyncPreviewCacheToClient(renderingCache, player.getUUID()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new SyncPreviewCacheToClient(renderingCache, player.getUUID()), player); } } } diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java b/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java index 8534cfe45d..25550013cf 100644 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java +++ b/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java @@ -1,16 +1,18 @@ package com.ldtteam.structurize.storage.rendering.types; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.client.RenderingCacheKey; import com.ldtteam.structurize.network.messages.SyncPreviewCacheToServer; import com.ldtteam.structurize.storage.StructurePacks; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.util.Mth; +import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.api.distmarker.Dist; @@ -72,14 +74,14 @@ public class BlueprintPreviewData private float overridePreviewTransparency = -1; /** - * Override blockstate for solid placeholders + * Setting for whether blocks render nice or not */ - private BlockState solidSubstitutionOverride = null; + private boolean renderBlocksNice = Structurize.getConfig().getClient() != null && Structurize.getConfig().getClient().renderPlaceholdersNice.get(); /** - * Setting for whether blocks render nice or not + * Override blockstate for solid placeholders */ - private boolean renderBlocksNice = Structurize.getConfig().getClient() != null && Structurize.getConfig().getClient().renderPlaceholdersNice.get(); + private BlockState solidSubstitutionOverride = null; /** * Default constructor to create a new setup. @@ -103,7 +105,7 @@ public BlueprintPreviewData(final boolean serverSyncEnabled) * Create blueprint preview data from byteBuf. * @param byteBuf the buffer data. */ - public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf) + public BlueprintPreviewData(final FriendlyByteBuf byteBuf) { this(byteBuf, true); } @@ -113,7 +115,7 @@ public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf) * @param byteBuf the buffer data. * @param serverSyncEnabled if false then wont send sync preview messages to server */ - public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf, final boolean serverSyncEnabled) + public BlueprintPreviewData(final FriendlyByteBuf byteBuf, final boolean serverSyncEnabled) { this.serverSyncEnabled = serverSyncEnabled; @@ -122,7 +124,7 @@ public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf, final boolean this.blueprintPath = byteBuf.readUtf(32767); if (StructurePacks.hasPack(packName)) { - blueprintFuture = StructurePacks.getBlueprintFuture(packName, blueprintPath, byteBuf.registryAccess()); + blueprintFuture = StructurePacks.getBlueprintFuture(packName, blueprintPath); } else { @@ -262,7 +264,7 @@ public void syncChangesToServer() { if (serverSyncEnabled && Structurize.getConfig().getClient().sharePreviews.get() && (blueprint == null || blueprint.getName() != null)) { - new SyncPreviewCacheToServer(this).sendToServer(); + Network.getNetwork().sendToServer(new SyncPreviewCacheToServer(this)); } } @@ -278,6 +280,38 @@ public void move(final BlockPos offset) } } + /** + * Check if the blueprint rendered should refresh the cache. + * @return true if so. + * @deprecated no longer needed + */ + @Deprecated(since = "1.20", forRemoval = true) + public boolean shouldRefresh() + { + return false; + } + + /** + * Tell the structurize renderer to refresh the cache. + * @deprecated switch to {@link #syncChangesToServer()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public void scheduleRefresh() + { + syncChangesToServer(); + } + + /** + * Get the placement settings for this instance. + * @return the placement settings with mirror and rotation. + * @deprecated see {@link #getRotationMirror()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public PlacementSettings getPlacementSettings() + { + return new PlacementSettings(rotationMirror.mirror(), rotationMirror.rotation()); + } + /** * Check if this is an invalid preview data object. * @return true if so. @@ -287,6 +321,28 @@ public boolean isEmpty() return blueprintFuture == null && blueprint == null; } + /** + * Get the rotation of the preview. + * @return the rotation. + * @deprecated see {@link #getRotationMirror()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public Rotation getRotation() + { + return rotationMirror.rotation(); + } + + /** + * Get the mirror of the preview. + * @return the mirror. + * @deprecated see {@link #getRotationMirror()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public Mirror getMirror() + { + return rotationMirror.mirror(); + } + /** * Get the placement settings for this instance. * @return the placement settings with mirror and rotation. @@ -392,8 +448,6 @@ public BlockState getSolidSubstitutionOverride() /** * Set the solid placeholder blockstate override, only updates when the renderer is recalculated - * - * @return */ public void setSolidSubstitutionOverride(final BlockState solidSubstitutionOverride) { diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BoxPreviewData.java b/src/main/java/com/ldtteam/structurize/storage/rendering/types/BoxPreviewData.java deleted file mode 100644 index c7d423aa67..0000000000 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BoxPreviewData.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.ldtteam.structurize.storage.rendering.types; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; - -import java.util.Objects; -import java.util.Optional; - -/** - * Preview data for box contexts. - */ -public final class BoxPreviewData -{ - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockPos.CODEC.fieldOf("pos1").forGetter(BoxPreviewData::pos1), - BlockPos.CODEC.fieldOf("pos2").forGetter(BoxPreviewData::pos2), - BlockPos.CODEC.optionalFieldOf("anchor").forGetter(BoxPreviewData::anchor)) - .apply(builder, BoxPreviewData::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(BlockPos.STREAM_CODEC, - BoxPreviewData::pos1, - BlockPos.STREAM_CODEC, - BoxPreviewData::pos2, - ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - BoxPreviewData::anchor, - BoxPreviewData::new); - private final BlockPos pos1; - private final BlockPos pos2; - private final Optional anchor; - - /** - * @param pos1 the first pos. - * @param pos2 the second pos. - * @param anchor the anchor of the box. - */ - public BoxPreviewData(BlockPos pos1, BlockPos pos2, Optional anchor) - { - this.pos1 = pos1; - this.pos2 = pos2; - this.anchor = anchor; - } - - private long expireTime = Long.MAX_VALUE; - - /** - * Update the corners. - * - * @param pos1 the new first corner. - * @param pos2 the new second corner. - * @return the new box with updated corners. - */ - public BoxPreviewData withCorners(final BlockPos pos1, final BlockPos pos2) - { - return new BoxPreviewData(pos1, pos2, anchor); - } - - /** - * Update the anchor position. - * - * @param anchor the new anchor position. - * @return the new box with updated anchor. - */ - public BoxPreviewData withAnchor(final Optional anchor) - { - return new BoxPreviewData(pos1, pos2, anchor); - } - - public boolean isExpired() - { - return System.currentTimeMillis() - expireTime > 0; - } - - public void setExpireTime(final int seconds) - { - expireTime = System.currentTimeMillis() + seconds * 1000; - } - - public BlockPos pos1() {return pos1;} - - public BlockPos pos2() {return pos2;} - - public Optional anchor() {return anchor;} - - @Override - public boolean equals(Object obj) - { - if (obj == this) - { - return true; - } - if (obj == null || obj.getClass() != this.getClass()) - { - return false; - } - var that = (BoxPreviewData) obj; - return Objects.equals(this.pos1, that.pos1) && - Objects.equals(this.pos2, that.pos2) && - Objects.equals(this.anchor, that.anchor); - } - - @Override - public int hashCode() - { - return Objects.hash(pos1, pos2, anchor); - } - - @Override - public String toString() - { - return "BoxPreviewData[" + - "pos1=" + pos1 + ", " + - "pos2=" + pos2 + ", " + - "anchor=" + anchor + ']'; - } -} diff --git a/src/main/java/com/ldtteam/structurize/tag/ModTags.java b/src/main/java/com/ldtteam/structurize/tag/ModTags.java index 8b05633e3d..0620b232ec 100644 --- a/src/main/java/com/ldtteam/structurize/tag/ModTags.java +++ b/src/main/java/com/ldtteam/structurize/tag/ModTags.java @@ -1,15 +1,17 @@ package com.ldtteam.structurize.tag; -import com.ldtteam.structurize.api.constants.Constants; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import org.jetbrains.annotations.NotNull; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; + public class ModTags { private ModTags() @@ -29,6 +31,6 @@ private ModTags() private static TagKey modTag(final ResourceKey> registry, @NotNull final String name) { - return TagKey.create(registry, Constants.resLocStruct(name)); + return TagKey.create(registry, Identifier.fromNamespaceAndPath(MOD_ID, name)); } } diff --git a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java index 4771fc29c7..851cf78d8b 100644 --- a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java @@ -1,35 +1,34 @@ package com.ldtteam.structurize.util; -import com.ldtteam.common.util.BlockToItemHelper; -import com.ldtteam.domumornamentum.client.model.data.MaterialTextureData; import com.ldtteam.domumornamentum.entity.block.MateriallyTexturedBlockEntity; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.placement.SimplePlacementContext; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; import com.ldtteam.structurize.tag.ModTags; +import net.minecraft.SharedConstants; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.Direction; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; import net.minecraft.tags.BlockTags; import net.minecraft.tags.ItemTags; -import net.minecraft.util.StaticCache2D; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.Entity; import net.minecraft.world.item.*; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; @@ -37,13 +36,8 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.Property; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.status.ChunkPyramid; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.chunk.status.ChunkStep; +import net.minecraft.world.level.chunk.*; +import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.levelgen.*; import net.minecraft.world.level.levelgen.blending.Blender; @@ -54,7 +48,7 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; import net.neoforged.neoforge.common.util.FakePlayer; -import net.neoforged.neoforge.registries.GameData; +import net.minecraft.core.registries.BuiltInRegistries; import org.jetbrains.annotations.Nullable; import java.text.MessageFormat; @@ -63,9 +57,7 @@ import java.util.function.Function; import java.util.function.Predicate; -import static com.ldtteam.domumornamentum.util.Constants.BLOCK_ENTITY_TEXTURE_DATA; import static com.ldtteam.structurize.tag.ModTags.GOOD_SOLID_FOR_PLACEHOLDER; - /** * Utility class for all Block type checking. */ @@ -105,12 +97,68 @@ public static void checkOrInit() { BuiltInRegistries.BLOCK.stream() .filter(BlockUtils::canBlockSurviveWithoutSupport) - .filter(block -> !block.defaultBlockState().canBeReplaced() && block.hasCollision && !(block instanceof Fallable) && !block.defaultBlockState().isAir() + .filter(block -> !block.defaultBlockState().canBeReplaced() && hasCollision(block.defaultBlockState()) && !(block instanceof Fallable) && !block.defaultBlockState().isAir() && !(block instanceof LiquidBlock) && !block.builtInRegistryHolder().is(ModTags.WEAK_SOLID_BLOCKS)) .forEach(trueSolidBlocks::add); } } + /** + * Updates the rotation of the structure depending on the input. + * + * @param rotation the rotation to be set. + * @return returns the Rotation object. + */ + public static Rotation getRotation(final int rotation) + { + switch (rotation) + { + case 1: + return Rotation.CLOCKWISE_90; + case 2: + return Rotation.CLOCKWISE_180; + case 3: + return Rotation.COUNTERCLOCKWISE_90; + default: + return Rotation.NONE; + } + } + + /** + * Gets a rotation from a block facing. + * + * @param facing the block facing. + * @return the int rotation. + */ + public static int getRotationFromFacing(final Direction facing) + { + switch (facing) + { + case SOUTH: + return 2; + case EAST: + return 1; + case WEST: + return 3; + default: + return 0; + } + } + + /** + * Get the filler block at a certain location. + * If block follows gravity laws return dirt. + * + * @param world the world the block is in. + * @param location the location it is at. + * @return the BlockState of the filler block. + */ + @Deprecated(forRemoval = true, since="1.18.2") + public static BlockState getSubstitutionBlockAtWorld(final Level world, final BlockPos location) + { + return Blocks.DIRT.defaultBlockState(); + } + /** * Get the filler block at a certain location. * @@ -171,10 +219,10 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca final SurfaceRules.Context ctx = new SurfaceRules.Context(serverLevel.getChunkSource().randomState().surfaceSystem(), serverLevel.getChunkSource().randomState(), chunk, - chunk.getOrCreateNoiseChunk(c -> createNoiseBiome(serverLevel, chunkGenerator, c)), + chunk.getOrCreateNoiseChunk(c -> createNoiseChunk(serverLevel, chunkGenerator, c)), serverLevel.getBiomeManager()::getBiome, - serverLevel.registryAccess().registryOrThrow(Registries.BIOME), - new WorldGenerationContext(chunkGenerator, serverLevel)); + new WorldGenerationContext(chunkGenerator, serverLevel), + null); final int locX = location.getX(); final int locY = location.getY(); @@ -185,7 +233,7 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca int waterHeight = Integer.MIN_VALUE; final MutableBlockPos temp = new MutableBlockPos(locX, locY, locZ); - for (int tempY = locY + 1; tempY <= chunk.getMaxBuildHeight() + 1; ++tempY) + for (int tempY = locY + 1; tempY <= chunk.getMaxY() + 1; ++tempY) { temp.setY(tempY); final BlockState bs = virtualBlocks == null ? chunk.getBlockState(temp) : @@ -204,7 +252,7 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca } } - for (int tempY = locY - 1; tempY >= chunk.getMinBuildHeight() - 1; --tempY) + for (int tempY = locY - 1; tempY >= chunk.getMinY() - 1; --tempY) { temp.setY(tempY); final BlockState bs = virtualBlocks == null ? chunk.getBlockState(temp) : @@ -219,14 +267,14 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca stoneDepthBelow = locY - stoneDepthBelow + 1; ctx.updateXZ(locX, locZ); - ctx.updateY(stoneDepthAbove, stoneDepthBelow, waterHeight, locX, locY, locZ); + ctx.updateY(stoneDepthAbove, stoneDepthBelow, waterHeight, locY); return generatorSettings.surfaceRule().apply(ctx).tryApply(locX, locY, locZ); } else if (generator instanceof FlatLevelSource chunkGenerator) { final List layers = chunkGenerator.settings().getLayers(); - final int locY = location.getY() - serverLevel.getMinBuildHeight(); + final int locY = location.getY() - serverLevel.getMinY(); if (locY >= 0 && locY < layers.size()) { return layers.get(locY); @@ -237,17 +285,34 @@ else if (generator instanceof FlatLevelSource chunkGenerator) return null; } - private static NoiseChunk createNoiseBiome( + private static NoiseChunk createNoiseChunk( final ServerLevel serverLevel, final NoiseBasedChunkGenerator chunkGenerator, final ChunkAccess chunk) { - final WorldGenRegion worldGenRegion = new OurWorldGenRegion(serverLevel, ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.SURFACE), chunk); + final NoiseGeneratorSettings settings = chunkGenerator.generatorSettings().value(); + return NoiseChunk.forChunk( + chunk, + serverLevel.getChunkSource().randomState(), + Beardifier.forStructuresInChunk(serverLevel.structureManager(), chunk.getPos()), + settings, + createGlobalFluidPicker(settings), + Blender.empty()); + } - return chunkGenerator.createNoiseChunk(chunk, - serverLevel.structureManager().forWorldGenRegion(worldGenRegion), - Blender.of(worldGenRegion), - serverLevel.getChunkSource().randomState()); + private static Aquifer.FluidPicker createGlobalFluidPicker(final NoiseGeneratorSettings settings) + { + final Aquifer.FluidStatus lavaStatus = new Aquifer.FluidStatus(-54, Blocks.LAVA.defaultBlockState()); + final int seaLevel = settings.seaLevel(); + final Aquifer.FluidStatus seaStatus = new Aquifer.FluidStatus(seaLevel, settings.defaultFluid()); + final Aquifer.FluidStatus emptyStatus = new Aquifer.FluidStatus(DimensionType.MIN_Y * 2, Blocks.AIR.defaultBlockState()); + return (x, y, z) -> { + if (SharedConstants.DEBUG_DISABLE_FLUID_GENERATION) + { + return emptyStatus; + } + return y < Math.min(-54, seaLevel) ? lavaStatus : seaStatus; + }; } /** @@ -261,7 +326,6 @@ public static boolean isWater(final BlockState iBlockState) return iBlockState.getBlock() == Blocks.WATER; } - @Deprecated(forRemoval = true, since = "1.21") private static Item getItem(final BlockState blockState) { final Block block = blockState.getBlock(); @@ -271,7 +335,7 @@ private static Item getItem(final BlockState blockState) } else if (block instanceof CropBlock) { - final ItemStack stack = ((CropBlock) block).getCloneItemStack(null, null, blockState); + final ItemStack stack = block.getCloneItemStack(null, null, blockState, false, null); if (stack != null) { return stack.getItem(); @@ -280,7 +344,7 @@ else if (block instanceof CropBlock) return Items.WHEAT_SEEDS; } // oh no... - else if (block instanceof FarmBlock || block instanceof DirtPathBlock) + else if (block instanceof FarmlandBlock || block instanceof DirtPathBlock) { return getItemFromBlock(Blocks.DIRT); } @@ -290,7 +354,7 @@ else if (block instanceof FireBlock) } else if (block instanceof FlowerPotBlock) { - return Items.FLOWER_POT; + return getItemFromBlock(((FlowerPotBlock) block).getPotted()); } else if (block == Blocks.BAMBOO_SAPLING) { @@ -302,10 +366,9 @@ else if (block == Blocks.BAMBOO_SAPLING) } } - @Deprecated(forRemoval = true, since = "1.21") private static Item getItemFromBlock(final Block block) { - return GameData.getBlockItemMap().get(block); + return Item.BY_BLOCK.get(block); } /** @@ -348,9 +411,13 @@ else if (worldEntity == null) { return false; } - else if (worldEntity instanceof final MateriallyTexturedBlockEntity mtbe && tileEntityData.contains(BLOCK_ENTITY_TEXTURE_DATA)) + else if (worldEntity instanceof MateriallyTexturedBlockEntity) { - return mtbe.getTextureData().equals(MaterialTextureData.CODEC.decode(NbtOps.INSTANCE, tileEntityData.get(BLOCK_ENTITY_TEXTURE_DATA)).getOrThrow().getFirst()); + CompoundTag tag = tileEntityData.copy(); + tag.putInt("x", worldEntity.getBlockPos().getX()); + tag.putInt("y", worldEntity.getBlockPos().getY()); + tag.putInt("z", worldEntity.getBlockPos().getZ()); + return Utils.nbtContains(tag, worldEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY))); } return true; } @@ -416,13 +483,13 @@ public static BlockState getBlockStateFromStack(final ItemStack stack, final Blo { return Blocks.AIR.defaultBlockState(); } - else if (stack.getItem() instanceof final BucketItem bucket) + else if (stack.getItem() instanceof BucketItem) { - return bucket.content.defaultFluidState().createLegacyBlock(); + return ((BucketItem) stack.getItem()).getContent().defaultFluidState().createLegacyBlock(); } - else if (stack.getItem() instanceof final BlockItem blockItem) + else if (stack.getItem() instanceof BlockItem) { - return blockItem.getBlock().defaultBlockState(); + return ((BlockItem) stack.getItem()).getBlock().defaultBlockState(); } return def; @@ -433,7 +500,6 @@ else if (stack.getItem() instanceof final BlockItem blockItem) * * @param blockState the block and state we are creating an ItemStack for. * @return ItemStack fromt the BlockState. - * @see BlockToItemHelper */ public static ItemStack getItemStackFromBlockState(final BlockState blockState) { @@ -472,7 +538,7 @@ public static boolean doBlocksMatch(final ItemStack block, final ServerLevel wor { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); final List itemList = - handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(world.registryAccess()), new SimplePlacementContext(false, RotationMirror.NONE)); + handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), new SimplePlacementContext(false, new PlacementSettings())); if (!itemList.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(itemList.get(0), block)) { isMatch = true; @@ -535,23 +601,18 @@ else if (item instanceof BlockItem) // place world.setBlock(here, Blocks.COBBLESTONE.defaultBlockState(), Block.UPDATE_CLIENTS); world.setBlock(here, newState, Constants.UPDATE_FLAG); - final BlockEntity blockEntity = world.getBlockEntity(here); - if (blockEntity != null) - { - blockEntity.applyComponentsFromItemStack(stackToPlace); - } targetBlock.setPlacedBy(world, here, newState, fakePlayer, stackToPlace); } else if (item instanceof BucketItem) { final Block sourceBlock = blockState.getBlock(); final BucketItem bucket = (BucketItem) item; - final Fluid fluid = bucket.content; + final Fluid fluid = bucket.getContent(); // place if (sourceBlock instanceof final LiquidBlockContainer liquidContainer) { - if (liquidContainer.canPlaceLiquid(fakePlayer, world, here, blockState, fluid)) + if (liquidContainer.canPlaceLiquid(null, world, here, blockState, fluid)) { liquidContainer.placeLiquid(world, here, blockState, fluid.defaultFluidState()); bucket.checkExtraContent(null, world, stackToPlace, here); @@ -606,7 +667,9 @@ public static BlockState getFluidForDimension(final Level world) } } } - return world == null || !world.dimensionType().ultraWarm() ? Blocks.WATER.defaultBlockState() : Blocks.LAVA.defaultBlockState(); + return world == null || !world.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, BlockPos.ZERO) + ? Blocks.WATER.defaultBlockState() + : Blocks.LAVA.defaultBlockState(); } /** @@ -663,116 +726,25 @@ public static List getBlockDrops(final Level world, final BlockPos co public static BlockState copyFirstCommonBlockStateProperties(final BlockState target, final BlockState propertiesOrigin) { BlockState newState = target; - for (final Property property : propertiesOrigin.getProperties()) + for (final Property property : propertiesOrigin.getProperties()) { if (target.hasProperty(property)) { - newState = copyProperty(propertiesOrigin, newState, property); + newState = newState.setValue(property, propertiesOrigin.getValue(property)); } } return newState; } - private static > BlockState copyProperty(final BlockState from, final BlockState to, final Property property) - { - return to.setValue(property, from.getValue(property)); - } - - private static class OurWorldGenRegion extends WorldGenRegion - { - private final StaticCache2D chunks; - private final ServerLevel level; - - private OurWorldGenRegion(final ServerLevel level, final ChunkStep step, final ChunkAccess chunk) - { - super(level, null, step, chunk); - final int chunkX = chunk.getPos().x; - final int chunkZ = chunk.getPos().z; - final int chunkRange = step.accumulatedDependencies().getRadius(); - - this.level = level; - chunks = StaticCache2D.create(chunkX, chunkZ, chunkRange, (x, z) -> { - ChunkAccess surroundingChunk = level.getChunk(x, z, ChunkStatus.SURFACE); - - if (surroundingChunk instanceof final ImposterProtoChunk imposterProtoChunk) - { - surroundingChunk = new ImposterProtoChunk(imposterProtoChunk.getWrapped(), true); - } - else if (surroundingChunk instanceof final LevelChunk levelChunk) - { - surroundingChunk = new ImposterProtoChunk(levelChunk, true); - } - - return surroundingChunk; - }); - } - - @Override - public boolean destroyBlock(BlockPos p_9550_, boolean p_9551_, @Nullable Entity p_9552_, int p_9553_) - { - return false; - } - - @Override - public boolean ensureCanWrite(BlockPos p_181031_) - { - return false; - } - - @Override - public boolean setBlock(BlockPos p_9539_, BlockState p_9540_, int p_9541_, int p_9542_) - { - return false; - } - - @Override - public boolean addFreshEntity(Entity p_9580_) - { - return false; - } - - @Override - public boolean removeBlock(BlockPos p_9547_, boolean p_9548_) - { - return false; - } - - @Override - public ChunkAccess getChunk(int p_9514_, int p_9515_, ChunkStatus p_331853_, boolean p_9517_) - { - return chunks.get(p_9514_, p_9515_); - } - - @Override - public boolean hasChunk(int p_9574_, int p_9575_) - { - return level.hasChunk(p_9574_, p_9575_); - } - - @Override - public boolean isOldChunkAround(ChunkPos pos, int radius) - { - final int minX = pos.x - radius; - final int maxX = pos.x + radius; - final int minZ = pos.z - radius; - final int maxZ = pos.z + radius; - - return chunks.contains(minX, minZ) && - chunks.contains(minX, maxZ) && - chunks.contains(maxX, minZ) && - chunks.contains(maxX, maxZ); - } - } - /** - * @return true iff block can exist without any support (cannot decay, {@link Block#canSurvive(BlockState, LevelReader, BlockPos)} always return true) + * @return true iff block can exist without any support (cannot decay, {@link Block#canSurvive(BlockState, LevelReader, BlockPos)} ()} always return true) */ public static boolean canBlockFloatInAir(final BlockState blockState) { - if (blockState.getBlock() instanceof LeavesBlock) + if (blockState.getBlock() instanceof final LeavesBlock leaves) { - return !blockState.isRandomlyTicking(); + return !isDecayingLeaves(blockState); } return trueSolidBlocks.contains(blockState.getBlock()); } @@ -798,12 +770,12 @@ public static boolean isLiquidOnlyBlock(final Block block) */ public static boolean isWeakSolidBlock(final BlockState blockState) { - if (blockState.getBlock() instanceof LeavesBlock) + if (blockState.getBlock() instanceof final LeavesBlock leaves) { - return blockState.isRandomlyTicking(); + return isDecayingLeaves(blockState); } - if (blockState.canBeReplaced() || !blockState.getBlock().hasCollision) + if (blockState.canBeReplaced() || !hasCollision(blockState)) { return false; } @@ -814,8 +786,7 @@ public static boolean isWeakSolidBlock(final BlockState blockState) public static boolean canBlockSurviveWithoutSupport(final Block block) { - // TODO: add tag - if (block instanceof FarmBlock || block instanceof DirtPathBlock) + if (block instanceof FarmlandBlock || block instanceof DirtPathBlock) { return true; } @@ -823,12 +794,23 @@ public static boolean canBlockSurviveWithoutSupport(final Block block) { return block.defaultBlockState().canSurvive(null, null); } - catch (final Exception e) + catch (final NullPointerException e) { + // Survival checks commonly inspect neighbouring states; registry scanning has neither. return false; } } + private static boolean hasCollision(final BlockState blockState) + { + return !blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty(); + } + + private static boolean isDecayingLeaves(final BlockState blockState) + { + return blockState.getValue(LeavesBlock.DISTANCE) == 7 && !blockState.getValue(LeavesBlock.PERSISTENT); + } + /** * Check if a block is a standard full block. * @param block the block to check. @@ -838,7 +820,7 @@ public static boolean isGoodFullBlock(final BlockState block) { try { - return block.getShape(null, null) == Shapes.block(); + return block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) == Shapes.block(); } catch (final Exception e) { @@ -868,4 +850,5 @@ public boolean isAnySolid() return canFloatInAir || isWeakSolid; } } + } diff --git a/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java b/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java index 1e3678db78..4804cfbad0 100644 --- a/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java +++ b/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java @@ -1,13 +1,14 @@ package com.ldtteam.structurize.util; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.management.Manager; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -15,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.stream.Collectors; /** * Change storage to store changes to an area to be able to undo them. @@ -97,13 +99,12 @@ public void addPostDataFor(final BlockPos place, final Level world) * * @param list the list of entities. */ - public void addEntities(final List list, final HolderLookup.Provider provider) + public void addEntities(final List list) { - list.stream().map(entity -> { - final CompoundTag tag = new CompoundTag(); - entity.save(tag); - return tag; - }).forEach(removedEntities::add); + removedEntities.addAll(list.stream() + .map(entity -> EntityNbtHelper.save(entity, entity.level().registryAccess())) + .filter(Objects::nonNull) + .toList()); } /** @@ -169,18 +170,18 @@ public boolean undo(final Level world, @Nullable final ChangeStorage undoStorage for (final CompoundTag data : removedEntities) { - final Optional> type = EntityType.by(data); - if (type.isPresent()) + final Entity entity = EntityType.loadEntityRecursive( + data, + world, + new EntitySpawnRequest(EntitySpawnReason.LOAD, false), + loaded -> loaded); + + if (entity != null) { - final Entity entity = type.get().create(world); - if (entity != null) + world.addFreshEntity(entity); + if (undoStorage != null) { - entity.load(data); - world.addFreshEntity(entity); - if (undoStorage != null) - { - undoStorage.addedEntities.add(entity); - } + undoStorage.addedEntities.add(entity); } } } diff --git a/src/main/java/com/ldtteam/structurize/util/EntityNbtHelper.java b/src/main/java/com/ldtteam/structurize/util/EntityNbtHelper.java new file mode 100644 index 0000000000..1ba07fef23 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/EntityNbtHelper.java @@ -0,0 +1,31 @@ +package com.ldtteam.structurize.util; + +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; +import org.jetbrains.annotations.Nullable; + +/** + * Adapters between legacy entity NBT workflows and the current value-tree API. + */ +public final class EntityNbtHelper +{ + private EntityNbtHelper() + { + } + + @Nullable + public static CompoundTag save(final Entity entity, final HolderLookup.Provider registries) + { + final TagValueOutput output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, registries); + return entity.save(output) ? output.buildResult() : null; + } + + public static void load(final Entity entity, final CompoundTag data, final HolderLookup.Provider registries) + { + entity.load(TagValueInput.create(ProblemReporter.DISCARDING, registries, data)); + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/IOPool.java b/src/main/java/com/ldtteam/structurize/util/IOPool.java index 6c2154154a..bce6ebcdf7 100755 --- a/src/main/java/com/ldtteam/structurize/util/IOPool.java +++ b/src/main/java/com/ldtteam/structurize/util/IOPool.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.util; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import org.jetbrains.annotations.NotNull; import java.util.concurrent.*; diff --git a/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java b/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java index 8eba038c40..4d47ec0740 100644 --- a/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java @@ -1,8 +1,10 @@ package com.ldtteam.structurize.util; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.api.util.ItemStackUtils; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.neoforged.neoforge.items.IItemHandler; + import java.util.ArrayList; import java.util.List; @@ -94,7 +96,8 @@ public static void transferIntoNextBestSlot(final ItemStack stack, final IItemHa public static void consumeStack(final ItemStack tempStack, final IItemHandler handler) { int count = tempStack.getCount(); - final ItemStack container = tempStack.getCraftingRemainingItem(); + final ItemStackTemplate remainingTemplate = tempStack.getItem().getCraftingRemainder(); + final ItemStack container = remainingTemplate == null ? ItemStack.EMPTY : remainingTemplate.create(); for (int i = 0; i < handler.getSlots(); i++) { diff --git a/src/main/java/com/ldtteam/structurize/util/ItemStackNbtHelper.java b/src/main/java/com/ldtteam/structurize/util/ItemStackNbtHelper.java new file mode 100644 index 0000000000..0755eccfc1 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/ItemStackNbtHelper.java @@ -0,0 +1,68 @@ +package com.ldtteam.structurize.util; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import org.jetbrains.annotations.Nullable; + +/** + * Compatibility boundary between Structurize's persisted item tags and Minecraft's data components. + */ +public final class ItemStackNbtHelper +{ + private ItemStackNbtHelper() + { + } + + public static boolean hasCustomTag(final ItemStack stack) + { + return stack.has(DataComponents.CUSTOM_DATA); + } + + @Nullable + public static CompoundTag getCustomTag(final ItemStack stack) + { + final CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + return customData == null ? null : customData.copyTag(); + } + + public static CompoundTag getOrCreateCustomTag(final ItemStack stack) + { + final CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + CustomData.set(DataComponents.CUSTOM_DATA, stack, tag); + return tag; + } + + public static void setCustomTag(final ItemStack stack, final CompoundTag tag) + { + if (tag.isEmpty()) + { + stack.remove(DataComponents.CUSTOM_DATA); + } + else + { + stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + } + } + + public static ItemStack readNetworkStack(final FriendlyByteBuf buf) + { + final RegistryFriendlyByteBuf registryBuf = new RegistryFriendlyByteBuf( + buf, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + return ItemStack.OPTIONAL_STREAM_CODEC.decode(registryBuf); + } + + public static void writeNetworkStack(final FriendlyByteBuf buf, final ItemStack stack) + { + final RegistryFriendlyByteBuf registryBuf = new RegistryFriendlyByteBuf( + buf, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + ItemStack.OPTIONAL_STREAM_CODEC.encode(registryBuf, stack); + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/JavaUtils.java b/src/main/java/com/ldtteam/structurize/util/JavaUtils.java index c910b3797c..5a6abd5f85 100644 --- a/src/main/java/com/ldtteam/structurize/util/JavaUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/JavaUtils.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.util; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/com/ldtteam/structurize/util/LanguageHandler.java b/src/main/java/com/ldtteam/structurize/util/LanguageHandler.java new file mode 100644 index 0000000000..297727bc21 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/LanguageHandler.java @@ -0,0 +1,142 @@ +package com.ldtteam.structurize.util; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import net.minecraft.client.Minecraft; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.api.distmarker.Dist; +import org.apache.commons.io.IOUtils; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.Map; + +/** + * Helper class for localization and sending player messages. + * Note that MineColonies is still using some of these, so it's not safe to delete yet. + */ +public final class LanguageHandler +{ + /** + * Private constructor to hide implicit one. + */ + private LanguageHandler() + { + // Intentionally left empty. + } + + /** + * Localize a string and use String.format(). + * + * @param inputKey translation key. + * @param args Objects for String.format(). + * @return Localized string. + */ + public static String format(final String inputKey, final Object... args) + { + final String key = inputKey.toLowerCase(Locale.US); + final String result; + if (args.length == 0) + { + result = Component.translatable(key).getString(); + } + else + { + result = Component.translatable(key, args).getString(); + } + return result.isEmpty() ? key : result; + } + + /** + * Translates key to readable string and formats it. + * + * @param key translation key + * @param format String.format() attributes + * @return formatted string + */ + public static String translateKeyWithFormat(final String key, final Object... format) + { + return String.format(translateKey(key), format); + } + + /** + * Translates key to readable string. + * + * @param key translation key + * @return readable string + */ + public static String translateKey(final String key) + { + return LanguageCache.getInstance().translateKey(key.toLowerCase(Locale.US)); + } + + /** + * Sets our cache to use mc default one. + */ + public static void setMClanguageLoaded() + { + LanguageCache.getInstance().isMCloaded = true; + LanguageCache.getInstance().languageMap = null; + } + + public static void loadLangPath(final String path) + { + LanguageCache.getInstance().load(path); + } + + private static class LanguageCache + { + private static final LanguageCache instance = new LanguageCache(); + private boolean isMCloaded = false; + private Map languageMap; + + private LanguageCache() + { + load("assets/structurize/lang/%s.json"); + } + + private void load(final String path) + { + final String defaultLocale = "en_us"; + + // Trust me, Minecraft.getInstance() can be null, when you run Data Generators! + String locale = "en_us"; + if (FMLEnvironment.getDist().isClient() && Minecraft.getInstance() != null) + { + locale = Minecraft.getInstance().options.languageCode; + } + + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(String.format(path, locale)); + if (is == null) + { + is = Thread.currentThread().getContextClassLoader().getResourceAsStream(String.format(path, defaultLocale)); + } + languageMap = new Gson().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), new TypeToken>() + {}.getType()); + + IOUtils.closeQuietly(is); + } + + private static LanguageCache getInstance() + { + return instance; + } + + private String translateKey(final String key) + { + if (isMCloaded) + { + return Language.getInstance().getOrDefault(key); + } + else + { + final String res = languageMap.get(key); + return res == null ? Language.getInstance().getOrDefault(key) : res; + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java b/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java new file mode 100644 index 0000000000..8fae3a2d07 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java @@ -0,0 +1,68 @@ +package com.ldtteam.structurize.util; + +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; + +/** + * Placement settings for the blueprints. + * @deprecated use {@link RotationMirror} + */ +public class PlacementSettings +{ + /** + * The currently used mirror of the settings. + */ + public Mirror mirror = Mirror.NONE; + + /** + * The currently used rotation of the settings. + */ + public Rotation rotation = Rotation.NONE; + + + /** + * Create a new empty placement settings object. + */ + public PlacementSettings() + { + /* + * Nothing to do here. + */ + } + + /** + * Create a new placement settings object with specific setting. + * @param mirror the mirror. + * @param rotation the rotation. + */ + public PlacementSettings(final Mirror mirror, final Rotation rotation) + { + this.mirror = mirror; + this.rotation = rotation; + } + + public Mirror getMirror() + { + return mirror; + } + + public void setMirror(final Mirror mirror) + { + this.mirror = mirror; + } + + public Rotation getRotation() + { + return rotation; + } + + public void setRotation(final Rotation rotation) + { + this.rotation = rotation; + } + + public RotationMirror getRotationMirror() + { + return RotationMirror.of(rotation, mirror); + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java b/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java index 8cc742bb42..71341e8caa 100644 --- a/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java +++ b/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.util; import com.ldtteam.structurize.blocks.ModBlocks; +import com.ldtteam.structurize.operations.ITickedWorldOperation; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; @@ -22,6 +23,11 @@ public class PlacerholderFillOperation implements ITickedWorldOperation */ private BlockPos startPos; + /** + * The current position to start iterating. + */ + private BlockPos currentPos; + /** * The end position. */ @@ -54,6 +60,7 @@ public PlacerholderFillOperation( final double yStretch, final double circleRadiusMult, final int heightOffset, final int minDistToBlocks) { this.startPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); + this.currentPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); this.yStretch = yStretch; this.circleRadiusMult = circleRadiusMult; this.heightOffset = heightOffset; diff --git a/src/main/java/com/ldtteam/structurize/api/RotationMirror.java b/src/main/java/com/ldtteam/structurize/util/RotationMirror.java similarity index 76% rename from src/main/java/com/ldtteam/structurize/api/RotationMirror.java rename to src/main/java/com/ldtteam/structurize/util/RotationMirror.java index 8063082650..fc05025081 100644 --- a/src/main/java/com/ldtteam/structurize/api/RotationMirror.java +++ b/src/main/java/com/ldtteam/structurize/util/RotationMirror.java @@ -1,16 +1,8 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.util; -import com.ldtteam.common.codec.Codecs; -import com.ldtteam.structurize.blueprints.FacingFixer; -import com.mojang.serialization.Codec; -import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; import net.minecraft.world.phys.Vec3; @@ -28,9 +20,6 @@ public enum RotationMirror MIR_R180(Rotation.CLOCKWISE_180, Mirror.FRONT_BACK), MIR_R270(Rotation.COUNTERCLOCKWISE_90, Mirror.FRONT_BACK); - public static final Codec CODEC = Codecs.forEnum(RotationMirror.class); - public static final StreamCodec STREAM_CODEC = ByteBufCodecs.VAR_INT.map(ordinal -> RotationMirror.values()[ordinal], RotationMirror::ordinal); - public static final RotationMirror[] MIRRORED = {MIR_NONE, MIR_R90, MIR_R180, MIR_R270}; public static final RotationMirror[] NOT_MIRRORED = {NONE, R90, R180, R270}; @@ -184,36 +173,6 @@ public Vec3 applyToPos(final Vec3 pos, final BlockPos pivot) return StructureTemplate.transform(pos, mirror, rotation, pivot); } - /** - * @param blockState blockState to transform - * @return transformed blockState using this rot+mir - * @deprecated use {@link #applyToBlockState(BlockState, Level, BlockPos)}, see vanilla methods for more info - */ - @Deprecated - public BlockState applyToBlockState(BlockState blockState) - { - if (isMirrored()) - { - blockState = FacingFixer.fixMirroredFacing(blockState.mirror(mirror), blockState); - } - return blockState.rotate(rotation); - } - - /** - * @param blockState blockState to transform - * @param level in which given blockState lives - * @param pos where the given blockState is in given level - * @return transformed blockState using this rot+mir - */ - public BlockState applyToBlockState(BlockState blockState, final Level level, final BlockPos pos) - { - if (isMirrored()) - { - blockState = FacingFixer.fixMirroredFacing(blockState.mirror(mirror), blockState); - } - return blockState.rotate(level, pos, rotation); - } - /** * @param end in which state we should end * @return end - this = what it takes from this to end diff --git a/src/main/java/com/ldtteam/structurize/util/ScanToolData.java b/src/main/java/com/ldtteam/structurize/util/ScanToolData.java index 896a02c59a..88f55d83b3 100644 --- a/src/main/java/com/ldtteam/structurize/util/ScanToolData.java +++ b/src/main/java/com/ldtteam/structurize/util/ScanToolData.java @@ -1,213 +1,190 @@ -package com.ldtteam.structurize.util; - -import com.ldtteam.structurize.component.ModDataComponents; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.CommandBlockEntity; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.function.UnaryOperator; - -/** - * Data representing a set of scans in the scan tool. - * @param slots the list of slot data. - * @param currentSlotId the currently selected slot id. - * @param commandPos the location of the linked command block. - * @param dimension the dimension of the linked command block. - */ -public record ScanToolData(List slots, int currentSlotId, - @Nullable BlockPos commandPos, @Nullable ResourceKey dimension) -{ - /** - * The number of scan slots. We keep 10 so that we can map them to 0-9 keys. - */ - public static final int NUM_SLOTS = 10; - - public static final Codec CODEC = RecordCodecBuilder.create(builder -> builder - .group(Slot.CODEC.listOf().fieldOf("slots").forGetter(data -> data.slots), - Codec.intRange(0, NUM_SLOTS - 1).fieldOf("current_slot").forGetter(ScanToolData::currentSlotId), - BlockPos.CODEC.optionalFieldOf("commands_pos").forGetter(data -> Optional.ofNullable(data.commandPos)), - Level.RESOURCE_KEY_CODEC.optionalFieldOf("dimension_key").forGetter(data -> Optional.ofNullable(data.dimension))) - .apply(builder, ScanToolData::fromCodec)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(Slot.STREAM_CODEC.apply(ByteBufCodecs.list()), data -> data.slots, - ByteBufCodecs.VAR_INT, ScanToolData::currentSlotId, - ByteBufCodecs.optional(BlockPos.STREAM_CODEC), data -> Optional.ofNullable(data.commandPos), - ByteBufCodecs.optional(ResourceKey.streamCodec(Registries.DIMENSION)), data -> Optional.ofNullable(data.dimension), - ScanToolData::fromCodec); - - public static ScanToolData EMPTY = new ScanToolData(List.of(), 1, null, null); - - private static ScanToolData fromCodec(final List slots, - final int currentSlotId, - final Optional commandPos, - final Optional> dimension) - { - return new ScanToolData(slots, currentSlotId, commandPos.orElse(null), dimension.orElse(null)); - } - - public ScanToolData(final List slots, - final int currentSlotId, - @Nullable final BlockPos commandPos, - @Nullable final ResourceKey dimension) - { - final List newSlots = new ArrayList<>(slots); - while (newSlots.size() > NUM_SLOTS || (!newSlots.isEmpty() && newSlots.getLast().equals(Slot.EMPTY))) - { - newSlots.removeLast(); - } - - this.slots = Collections.unmodifiableList(newSlots); - this.currentSlotId = currentSlotId; - this.commandPos = commandPos; - this.dimension = dimension; - } - - /** - * Gets the currently selected slot - * @return the slot data for the current slot - */ - public Slot currentSlot() - { - return currentSlotId < slots.size() ? slots.get(currentSlotId) : Slot.EMPTY; - } - - /** - * Saves the specified data in the current slot. - * @param data the new slot data. - * @return the new {@link ScanToolData}. - */ - public ScanToolData withCurrentSlot(@Nullable final Slot data) - { - List newSlots = new ArrayList<>(slots); - while (currentSlotId >= newSlots.size()) - { - newSlots.add(Slot.EMPTY); - } - newSlots.set(currentSlotId, data == null ? Slot.EMPTY : data); - - return new ScanToolData(newSlots, currentSlotId, commandPos, dimension); - } - - /** - * Moves to the next slot, wrapping back to the start if needed - */ - public ScanToolData nextSlot() - { - return moveTo((currentSlotId() + 1) % NUM_SLOTS); - } - - /** - * Moves to the previous slot, wrapping to the end if needed - */ - public ScanToolData prevSlot() - { - return moveTo((currentSlotId() + NUM_SLOTS - 1) % NUM_SLOTS); - } - - /** - * Moves to the specified slot number - * @param slot the new slot number - */ - public ScanToolData moveTo(final int slot) - { - return new ScanToolData(slots, slot, commandPos, dimension); - } - - /** - * Sets the command block position and dimension. - * @param commandBlock the command block entity. - * @return the updated {@link ScanToolData}. - */ - public ScanToolData withCommandBlock(@Nullable final CommandBlockEntity commandBlock) - { - return commandBlock == null - ? new ScanToolData(slots, currentSlotId, (BlockPos) null, null) - : new ScanToolData(slots, currentSlotId, commandBlock.getBlockPos(), commandBlock.getLevel().dimension()); - } - - /** - * Gets the {@link ScanToolData} from an {@link ItemStack}. - * @param stack the stack to query. - * @return the associated data or immutable empty instance. - */ - public static ScanToolData readFromItemStack(final ItemStack stack) - { - return stack.getOrDefault(ModDataComponents.SCAN_TOOL, EMPTY); - } - - /** - * Writes the {@link ScanToolData} into an {@link ItemStack}. - * @param itemStack the stack to save into. - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.SCAN_TOOL, this); - } - - /** - * Modifies the {@link ScanToolData} on an {@link ItemStack}. - * @param stack the stack to update. - * @param updater the update actions to apply. - * @return the updated data (also stored on the stack). - */ - public static ScanToolData updateItemStack(final ItemStack stack, final UnaryOperator updater) - { - final ScanToolData data = updater.apply(readFromItemStack(stack)); - data.writeToItemStack(stack); - return data; - } - - /** - * Data for one scan slot - */ - public record Slot(@NotNull String name, @NotNull BoxPreviewData box) - { - public static final Slot EMPTY = new Slot("", new BoxPreviewData(BlockPos.ZERO, BlockPos.ZERO, Optional.empty())); - - public static final Codec CODEC = RecordCodecBuilder.create(builder -> builder - .group(Codec.STRING.fieldOf("name").forGetter(Slot::name), - BoxPreviewData.CODEC.fieldOf("box").forGetter(Slot::box)) - .apply(builder, Slot::new)); - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, Slot::name, - BoxPreviewData.STREAM_CODEC, Slot::box, - Slot::new); - - /** - * Updates the name of the slot. - * @param name the new name. - * @return the {@link Slot} with the updated value. - */ - public Slot withName(final String name) - { - return new Slot(name, box); - } - - /** - * Updates the box of the slot. - * @param box the new box. - * @return the {@link Slot} with the updated value. - */ - public Slot withBox(final BoxPreviewData box) - { - return new Slot(name, box); - } - } -} +package com.ldtteam.structurize.util; + +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; + +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; + +/** + * Data representing a set of scans in the scan tool. + * This is deliberately lazy and accesses only the parts of the tag you actually ask for. + */ +public class ScanToolData +{ + /** + * The number of scan slots. We keep 10 so that we can map them to 0-9 keys. + */ + public static final int NUM_SLOTS = 10; + + private static final String NBT_SLOTS = MOD_ID + ":slots"; + private static final String NBT_CURRENT = MOD_ID + ":cur"; + + private final CompoundTag tag; + + /** + * Load from a tag + * @param tag the tag + */ + public ScanToolData(@NotNull CompoundTag tag) + { + this.tag = tag; + } + + /** + * Gets the internal tag used to store data. Don't fiddle with this. + * @return the tag + */ + @NotNull + public CompoundTag getInternalTag() + { + return this.tag; + } + + /** + * Gets the currently selected slot number + * @return the slot number + */ + public int getCurrentSlotId() + { + // the default slot is #1 so that we can treat slot 0 as if it were slot 10 (but we still call it slot 0) + return this.tag.contains(NBT_CURRENT) + ? Math.max(0, Math.min(NUM_SLOTS - 1, this.tag.getIntOr(NBT_CURRENT, 1))) + : 1; + } + + /** + * Gets the currently selected slot + * @return the slot data for the current slot + */ + @NotNull + public Slot getCurrentSlotData() + { + final int current = getCurrentSlotId(); + final ListTag slots = tag.getListOrEmpty(NBT_SLOTS); + final CompoundTag slotTag = current < slots.size() ? slots.getCompound(current).orElse(null) : null; + return new Slot(Objects.requireNonNullElse(slotTag, new CompoundTag())); + } + + /** + * Saves the specified data in the current slot + * @param data the new slot data + */ + public void setCurrentSlotData(@Nullable final Slot data) + { + final int current = getCurrentSlotId(); + final ListTag slots = tag.getListOrEmpty(NBT_SLOTS); + while (current >= slots.size()) slots.add(new CompoundTag()); + slots.set(current, data == null ? new CompoundTag() : data.write(new CompoundTag())); + tag.put(NBT_SLOTS, slots); + } + + /** + * Moves to the next slot, wrapping back to the start if needed + */ + public void nextSlot() + { + moveTo((getCurrentSlotId() + 1) % NUM_SLOTS); + } + + /** + * Moves to the previous slot, wrapping to the end if needed + */ + public void prevSlot() + { + moveTo((getCurrentSlotId() + NUM_SLOTS - 1) % NUM_SLOTS); + } + + /** + * Moves to the specified slot number + * @param slot the new slot number + */ + public void moveTo(final int slot) + { + this.tag.putInt(NBT_CURRENT, slot); + } + + + /** + * Data for one scan slot + */ + public static class Slot + { + private final String name; + private final BoxPreviewData box; + + /** + * Construct directly + * @param name the schematic name + * @param box the schematic box + */ + public Slot(@NotNull final String name, + @NotNull final BoxPreviewData box) + { + this.name = name; + this.box = box; + } + + /** + * Load from tag + * @param tag the tag + */ + public Slot(@NotNull final CompoundTag tag) + { + final BlockPos corner1 = BlockPosUtil.readFromNBT(tag, "c1"); + final BlockPos corner2 = BlockPosUtil.readFromNBT(tag, "c2"); + final Optional anchor = tag.contains("a") + ? Optional.of(BlockPosUtil.readFromNBT(tag, "a")) + : Optional.empty(); + this.box = new BoxPreviewData(corner1, corner2, anchor); + + this.name = tag.getStringOr("n", ""); + } + + /** + * Serialize + * @param tag target tag + * @return the same tag (for convenience) + */ + public CompoundTag write(@NotNull final CompoundTag tag) + { + BlockPosUtil.writeToNBT(tag, "c1", this.box.getPos1()); + BlockPosUtil.writeToNBT(tag, "c2", this.box.getPos2()); + if (this.box.getAnchor().isPresent()) + { + BlockPosUtil.writeToNBT(tag, "a", this.box.getAnchor().get()); + } + else + { + tag.remove("a"); + } + tag.putString("n", this.name); + return tag; + } + + public boolean isEmpty() + { + return this.name.isEmpty(); + } + + @NotNull + public BoxPreviewData getBox() + { + return this.box; + } + + @NotNull + public String getName() + { + return this.name; + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/api/TagManager.java b/src/main/java/com/ldtteam/structurize/util/TagManager.java similarity index 91% rename from src/main/java/com/ldtteam/structurize/api/TagManager.java rename to src/main/java/com/ldtteam/structurize/util/TagManager.java index c3e19e35ea..58bda74e28 100644 --- a/src/main/java/com/ldtteam/structurize/api/TagManager.java +++ b/src/main/java/com/ldtteam/structurize/util/TagManager.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.util; import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; @@ -6,8 +6,8 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static com.ldtteam.structurize.api.constants.Constants.GROUNDLEVEL_TAG; -import static com.ldtteam.structurize.api.constants.Constants.INVISIBLE_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.GROUNDLEVEL_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.INVISIBLE_TAG; /** * Handles tags. diff --git a/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java b/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java new file mode 100644 index 0000000000..1c0b88d7bd --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java @@ -0,0 +1,389 @@ +package com.ldtteam.structurize.util; + +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.network.messages.UpdateClientRender; +import com.ldtteam.structurize.operations.ITickedWorldOperation; +import com.ldtteam.structurize.placement.BlockPlacementResult; +import com.ldtteam.structurize.placement.SimplePlacementContext; +import com.ldtteam.structurize.placement.StructurePhasePlacementResult; +import com.ldtteam.structurize.placement.StructurePlacer; +import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; +import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; +import com.mojang.authlib.GameProfile; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.BucketPickup; +import net.minecraft.world.level.block.DoorBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.neoforged.neoforge.common.util.FakePlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Locale; +import java.util.UUID; + +import static com.ldtteam.structurize.placement.AbstractBlueprintIterator.NULL_POS; + +/** + * Contains an operation, as remove block, replace block, place structure, etc. + * + * @deprecated Superseded by {@link com.ldtteam.structurize.operations.PlaceStructureOperation} + */ +@Deprecated(forRemoval = true) +public class TickedWorldOperation implements ITickedWorldOperation +{ + /** + * Scan tool operation types. + */ + public enum OperationType + { + REMOVE_BLOCK, + REPLACE_BLOCK, + REMOVE_ENTITY, + SCAN, + PLACE_STRUCTURE, + UNDO, + REDO, + LOAD_AND_OPERATE + } + + /** + * The operation type. + */ + private final OperationType operation; + + /** + * The current position to start iterating. + */ + private BlockPos startPos; + + /** + * The current position to start iterating. + */ + private BlockPos currentPos; + + /** + * The end position. + */ + private final BlockPos endPos; + + /** + * The creator of the operation. + */ + @Nullable + private Player player = null; + + /** + * The changeStorage associated to this operation.. + */ + private final ChangeStorage storage; + + /** + * The undostorage for undo's + */ + private ChangeStorage undoStorage; + + /** + * The block to remove or to replace. + */ + private final ItemStack firstBlock; + + /** + * The block to replace it with. + */ + private final ItemStack secondBlock; + + /** + * The structure wrapper if structure place. + */ + private final StructurePlacer placer; + + /** + * The phase the placmeent is in. + */ + private int structurePhase = 0; + + /** + * Operation percentage. + */ + private int pct; + + /** + * Create a ScanToolOperation. + * + * @param type the type. + * @param startPos the start position. + * @param endPos the end position. + * @param player the player who triggered the event. + * @param firstBlock the block being altered. + * @param secondBlock the block it will be replaced with. + * @param pct the percentage of positions to execute the operation on. + */ + public TickedWorldOperation( + final OperationType type, + final BlockPos startPos, + final BlockPos endPos, + @Nullable final Player player, + final ItemStack firstBlock, + final ItemStack secondBlock, + final int pct) + { + this.operation = type; + this.startPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); + this.currentPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); + this.endPos = new BlockPos(Math.max(startPos.getX(), endPos.getX()), Math.max(startPos.getY(), endPos.getY()), Math.max(startPos.getZ(), endPos.getZ())); + this.player = player; + this.firstBlock = firstBlock; + this.secondBlock = secondBlock; + final Component component = switch (type) + { + case REMOVE_BLOCK -> Component.translatable("com.ldtteam.structurize." + type.toString().toLowerCase(Locale.US), firstBlock.getDisplayName()); + case REPLACE_BLOCK -> Component.translatable("com.ldtteam.structurize." + type.toString().toLowerCase(Locale.US), firstBlock.getDisplayName(), secondBlock.getDisplayName()); + default -> Component.literal(type.toString()); + }; + this.storage = new ChangeStorage(component, player != null ? player.getUUID() : UUID.randomUUID()); + this.placer = null; + this.pct = pct; + } + + /** + * Create a ScanToolOperation for an UNDO. + * + * @param storage the storage for the UNDO. + * @param player the player. + */ + public TickedWorldOperation(final ChangeStorage storage, @Nullable final Player player, final OperationType operation) + { + this.operation = operation; + this.startPos = BlockPos.ZERO; + this.currentPos = BlockPos.ZERO; + this.endPos = BlockPos.ZERO; + this.player = player; + this.firstBlock = ItemStack.EMPTY; + this.secondBlock = ItemStack.EMPTY; + this.storage = storage; + storage.resetUnRedo(); + if (operation == OperationType.UNDO && storage.getOperation().toString().indexOf(TickedWorldOperation.OperationType.UNDO.toString()) != 0) + { + undoStorage = new ChangeStorage(Component.translatable("com.ldtteam.structurize." + operation.toString().toLowerCase(Locale.US), storage.getOperation()), player != null ? player.getUUID() : UUID.randomUUID()); + } + this.placer = null; + } + + /** + * Create a ScanToolOperation for an structure placement. + * + * @param placer the structure for the placement.. + * @param player the player. + */ + public TickedWorldOperation(final StructurePlacer placer, @Nullable final Player player) + { + this.operation = OperationType.PLACE_STRUCTURE; + this.startPos = BlockPos.ZERO; + this.currentPos = NULL_POS; + this.endPos = BlockPos.ZERO; + this.player = player; + this.firstBlock = ItemStack.EMPTY; + this.secondBlock = ItemStack.EMPTY; + this.storage = new ChangeStorage(Component.translatable("com.ldtteam.structurize." + operation.toString().toLowerCase(Locale.US), placer.getHandler().getBluePrint().getName()), player != null ? player.getUUID() : UUID.randomUUID()); + this.placer = placer; + } + + @Override + public boolean apply(final ServerLevel world) + { + if (placer != null && !placer.isReady()) + { + return false; + } + + if (player != null && player.level().dimension() != world.dimension()) + { + return false; + } + + if (operation == OperationType.UNDO) + { + return storage.undo(world, undoStorage); + } + + if (operation == OperationType.REDO) + { + return storage.redo(world); + } + + if (operation == OperationType.PLACE_STRUCTURE) + { + if (placer.getHandler().getWorld().dimension().identifier().equals(world.dimension().identifier())) + { + StructurePhasePlacementResult result; + switch (structurePhase) + { + case 0: + //structure + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.BLOCK_PLACEMENT, + () -> placer.getIterator().increment((info, pos, handler) -> !BlockUtils.canBlockFloatInAir(info.getBlockInfo().getState())), false); + + currentPos = result.getIteratorPos(); + break; + case 1: + // weak solid + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.BLOCK_PLACEMENT, + () -> placer.getIterator().increment((info, pos, handler) -> !BlockUtils.isWeakSolidBlock(info.getBlockInfo().getState())), false); + + currentPos = result.getIteratorPos(); + break; + case 2: + //water + result = placer.clearWaterStep(world, currentPos); + currentPos = result.getIteratorPos(); + if (result.getBlockResult().getResult() == BlockPlacementResult.Result.FINISHED) + { + currentPos = placer.getIterator().getProgressPos(); + } + break; + case 3: + // not solid + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.BLOCK_PLACEMENT, + () -> placer.getIterator().increment((info, pos, handler) -> BlockUtils.isAnySolid(info.getBlockInfo().getState())), false); + currentPos = result.getIteratorPos(); + break; + default: + // entities + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.SPAWN_ENTITY, + () -> placer.getIterator().increment((info, pos, handler) -> info.getEntities().length == 0), true); + currentPos = result.getIteratorPos(); + break; + } + + if (result.getBlockResult().getResult() == BlockPlacementResult.Result.FINISHED) + { + structurePhase++; + if (structurePhase > 4) + { + structurePhase = 0; + currentPos = null; + placer.getHandler().onCompletion(); + } + } + + return currentPos == null; + } + return false; + } + + return run(world); + } + + /** + * Run the operation up to a max count. + * + * @param world the world to run it in. + * @return true if finished. + */ + private boolean run(final ServerLevel world) + { + final FakePlayer fakePlayer = new FakePlayer(world, new GameProfile(player == null ? UUID.randomUUID() : player.getUUID(), "structurizefakeplayer")); + int count = 0; + for (int y = currentPos.getY(); y <= endPos.getY(); y++) + { + for (int x = currentPos.getX(); x <= endPos.getX(); x++) + { + for (int z = currentPos.getZ(); z <= endPos.getZ(); z++) + { + final BlockPos here = new BlockPos(x, y, z); + final BlockState blockState = world.getBlockState(here); + final BlockEntity tileEntity = world.getBlockEntity(here); + boolean isMatch = false; + + if (firstBlock.getItem() == Items.AIR && blockState.isAir()) + { + isMatch = true; + } + else + { + final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); + final List itemList = + handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), new SimplePlacementContext(this.placer.getHandler().fancyPlacement(), this.placer.getHandler().getRotationMirror())); + if (!itemList.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(itemList.get(0), firstBlock)) + { + isMatch = true; + } + } + + if (isMatch) + { + if (pct < 100 && fakePlayer.getRandom().nextInt(100) > pct) + { + continue; + } + + if (blockState.getBlock() instanceof DoorBlock && blockState.getValue(DoorBlock.HALF) == DoubleBlockHalf.UPPER) + { + continue; + } + count++; + + storage.addPreviousDataFor(here, world); + if (operation != OperationType.REPLACE_BLOCK && (blockState.getBlock() instanceof BucketPickup + || BlockUtils.isLiquidOnlyBlock(blockState.getBlock()))) + { + BlockUtils.removeFluid(world, here); + if (firstBlock.getItem() instanceof BucketItem && !BlockUtils.isLiquidOnlyBlock(blockState.getBlock())) + { + if (count >= Structurize.getConfig().getServer().maxOperationsPerTick.get()) + { + currentPos = new BlockPos(x, y, z); + return false; + } + else + { + continue; + } + } + } + + if (operation == OperationType.REPLACE_BLOCK) + { + BlockUtils.handleCorrectBlockPlacement(world, fakePlayer, secondBlock, blockState, here); + } + else + { + world.removeBlock(here, false); + } + + storage.addPostDataFor(here, world); + + if (count >= Structurize.getConfig().getServer().maxOperationsPerTick.get()) + { + currentPos = new BlockPos(x, y, z); + return false; + } + } + } + currentPos = new BlockPos(x, y, startPos.getZ()); + } + currentPos = new BlockPos(startPos.getX(), y, startPos.getZ()); + } + Network.getNetwork().sendToEveryone(new UpdateClientRender(startPos, endPos)); + + return true; + } + + @Override + public ChangeStorage getChangeStorage() + { + return this.storage; + } +} \ No newline at end of file diff --git a/src/main/java/com/ldtteam/structurize/util/WorldRenderMacros.java b/src/main/java/com/ldtteam/structurize/util/WorldRenderMacros.java deleted file mode 100644 index cf569e2e89..0000000000 --- a/src/main/java/com/ldtteam/structurize/util/WorldRenderMacros.java +++ /dev/null @@ -1,1263 +0,0 @@ -package com.ldtteam.structurize.util; - -import com.ldtteam.structurize.client.BlueprintHandler; -import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.blaze3d.vertex.VertexFormat; -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.MultiBufferSource.BufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.entity.EntityRenderDispatcher; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.util.Mth; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage; -import org.joml.Matrix4f; -import org.joml.Matrix4fStack; -import org.lwjgl.opengl.GL30C; - -import java.util.Collection; -import java.util.List; - -public abstract class WorldRenderMacros -{ - // 4 chunks squared - public static final int MAX_DEBUG_TEXT_RENDER_DIST_SQUARED = Mth.square(4 * 16); - public static final RenderType LINES = RenderTypes.LINES; - public static final RenderType LINES_WITH_WIDTH = RenderTypes.LINES_WITH_WIDTH; - public static final RenderType LINES_WITH_WIDTH_DEPTH_INVERT = RenderTypes.LINES_WITH_WIDTH_DEPTH_INVERT; - public static final RenderType GLINT_LINES = RenderTypes.GLINT_LINES; - public static final RenderType GLINT_LINES_WITH_WIDTH = RenderTypes.GLINT_LINES_WITH_WIDTH; - public static final RenderType COLORED_TRIANGLES = RenderTypes.COLORED_TRIANGLES; - public static final RenderType COLORED_TRIANGLES_NC_ND = RenderTypes.COLORED_TRIANGLES_NC_ND; - public static final Stage STAGE_FOR_LINES = Stage.AFTER_ENTITIES; - public static final float DEFAULT_LINE_WIDTH = 0.025f; - - public Minecraft mc; - public RenderLevelStageEvent event; - public LocalPlayer clientPlayer; - public BufferSource bufferSource; - public PoseStack poseStack; - public DeltaTracker deltaTracker; - public ClientLevel clientLevel; - public ItemStack mainHandItem; - public Vec3 cameraPosition; - /** - * In chunks - */ - public int clientRenderDist; - - /** - * Call this from event handler - * - * @param event - */ - public void renderWorldLastEvent(final RenderLevelStageEvent e) - { - mc = Minecraft.getInstance(); - event = e; - clientPlayer = mc.player; - if (clientPlayer == null) // server login phase - { - return; - } - - bufferSource = mc.renderBuffers().bufferSource(); - poseStack = event.getPoseStack(); - deltaTracker = event.getPartialTick(); - clientLevel = mc.level; - mainHandItem = clientPlayer.getMainHandItem(); - cameraPosition = event.getCamera().getPosition(); - clientRenderDist = mc.options.renderDistance().get(); - - final Matrix4fStack mvMatrix = RenderSystem.getModelViewStack(); - mvMatrix.pushMatrix(); - mvMatrix.identity(); - mvMatrix.mul(event.getModelViewMatrix()); - RenderSystem.applyModelViewMatrix(); - - renderWithinContext(event.getStage()); - - RenderSystem.getModelViewStack().popMatrix(); - RenderSystem.applyModelViewMatrix(); - } - - /** - * This is called with properly prepared context. Do here what you want - * - * @param stage render world stage - */ - protected abstract void renderWithinContext(Stage stage); - - /** - * Moved pose context to camera and given pos - * - * @see #popPose() - */ - public final void pushPoseCameraToPos(final BlockPos pos) - { - poseStack.pushPose(); - poseStack.translate(pos.getX() - cameraPosition.x(), pos.getY() - cameraPosition.y(), pos.getZ() - cameraPosition.z()); - } - - public final void popPose() - { - poseStack.popPose(); - } - - public void pushShaderMvMatrixFromPose() - { - final Matrix4fStack mvMatrix = RenderSystem.getModelViewStack(); - mvMatrix.pushMatrix(); - mvMatrix.mul(poseStack.last().pose()); - RenderSystem.applyModelViewMatrix(); - } - - public void popShaderMvMatrix() - { - RenderSystem.getModelViewStack().popMatrix(); - RenderSystem.applyModelViewMatrix(); - } - - /** - * @return true if given aabb can be in any way seen by camera - */ - public final boolean isVisible(final AABB aabb) - { - return event.getFrustum().isVisible(aabb); - } - - /** - * @return true if given pos can be in any way seen by camera - */ - public final boolean isVisible(final BlockPos pos) - { - return isVisible(pos, pos); - } - - /** - * @return true if given box can be in any way seen by camera - */ - public final boolean isVisible(final BlockPos posA, final BlockPos posB) - { - return event.getFrustum() - .cubeInFrustum(Math.min(posA.getX(), posB.getX()), - Math.min(posA.getY(), posB.getY()), - Math.min(posA.getZ(), posB.getZ()), - Math.max(posA.getX(), posB.getX()) + 1, - Math.max(posA.getY(), posB.getY()) + 1, - Math.max(posA.getZ(), posB.getZ()) + 1); - } - - /** - * Draw a blueprint at given pos. - * - * @param previewData the blueprint and context to draw. - * @param pos position to render at - */ - public final void renderBlueprint(final BlueprintPreviewData blueprint, final BlockPos pos) - { - BlueprintHandler.getInstance().draw(blueprint, pos, event); - } - - /** - * Draw a blueprint at list of given pos. - * - * @param previewData the blueprint and context to draw. - * @param points list of positions to render at - */ - public final void renderBlueprint(final BlueprintPreviewData blueprint, final Collection points) - { - BlueprintHandler.getInstance().drawAtListOfPositions(blueprint, points, event); - } - - /** - * Render a black box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderBlackLineBox(final BlockPos posA, final BlockPos posB, final float lineWidth) - { - renderLineBox(LINES_WITH_WIDTH, posA, posB, 0x00, 0x00, 0x00, 0xff, lineWidth); - } - - /** - * Render a red glint box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderRedGlintLineBox(final BlockPos posA, final BlockPos posB, final float lineWidth) - { - renderLineBox(GLINT_LINES_WITH_WIDTH, posA, posB, 0xff, 0x0, 0x0, 0xff, lineWidth); - } - - /** - * Render a white box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderWhiteLineBox(final BlockPos posA, final BlockPos posB, final float lineWidth) - { - renderLineBox(LINES_WITH_WIDTH, posA, posB, 0xff, 0xff, 0xff, 0xff, lineWidth); - } - - /** - * Render a colored box around from aabb - * - * @param aabb the box - */ - public final void renderLineAABB(final RenderType renderType, final AABB aabb, final int argbColor, final float lineWidth) - { - renderLineAABB(renderType, - aabb, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - lineWidth); - } - - /** - * Render a colored box around from aabb - * - * @param aabb the box - */ - public final void renderLineAABB(final RenderType renderType, - final AABB aabb, - final int red, - final int green, - final int blue, - final int alpha, - final float lineWidth) - { - renderLineBox(renderType, - (float) aabb.minX, - (float) aabb.minY, - (float) aabb.minZ, - (float) aabb.maxX, - (float) aabb.maxY, - (float) aabb.maxZ, - red, - green, - blue, - alpha, - lineWidth); - } - - /** - * Render a colored box around position - * - * @param pos The Position - */ - public final void renderLineBox(final RenderType renderType, - final BlockPos pos, - final int argbColor, - final float lineWidth) - { - renderLineBox(renderType, - pos, - pos, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - lineWidth); - } - - /** - * Render a colored box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderLineBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int argbColor, - final float lineWidth) - { - renderLineBox(renderType, - posA, - posB, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - lineWidth); - } - - /** - * Render a box around two positions - * - * @param posA First position - * @param posB Second position - */ - public final void renderLineBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int red, - final int green, - final int blue, - final int alpha, - final float lineWidth) - { - renderLineBox(renderType, - Math.min(posA.getX(), posB.getX()), - Math.min(posA.getY(), posB.getY()), - Math.min(posA.getZ(), posB.getZ()), - Math.max(posA.getX(), posB.getX()) + 1, - Math.max(posA.getY(), posB.getY()) + 1, - Math.max(posA.getZ(), posB.getZ()) + 1, - red, - green, - blue, - alpha, - lineWidth); - } - - /** - * Render a box around two positions - * - * @param posA First position - * @param posB Second position - */ - public final void renderLineBox(final RenderType renderType, - float minX, - float minY, - float minZ, - float maxX, - float maxY, - float maxZ, - final int red, - final int green, - final int blue, - final int alpha, - final float lineWidth) - { - if (alpha == 0) - { - return; - } - - final float halfLine = lineWidth / 2.0f; - minX -= halfLine; - minY -= halfLine; - minZ -= halfLine; - final float minX2 = minX + lineWidth; - final float minY2 = minY + lineWidth; - final float minZ2 = minZ + lineWidth; - - maxX += halfLine; - maxY += halfLine; - maxZ += halfLine; - final float maxX2 = maxX - lineWidth; - final float maxY2 = maxY - lineWidth; - final float maxZ2 = maxZ - lineWidth; - - populateRenderLineBox(minX, minY, minZ, minX2, minY2, minZ2, maxX, maxY, maxZ, maxX2, maxY2, maxZ2, red, green, blue, alpha, poseStack.last().pose(), bufferSource.getBuffer(renderType)); - } - - // TODO: ebo this, does vanilla have any ebo things? - protected final void populateRenderLineBox(final float minX, - final float minY, - final float minZ, - final float minX2, - final float minY2, - final float minZ2, - final float maxX, - final float maxY, - final float maxZ, - final float maxX2, - final float maxY2, - final float maxZ2, - final int red, - final int green, - final int blue, - final int alpha, - final Matrix4f m, - final VertexConsumer buf) - { - // z plane - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - // x plane - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - - // y plane - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - } - - public final void renderBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int argbColor) - { - renderBox(renderType, - posA, - posB, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff); - } - - public final void renderBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int red, - final int green, - final int blue, - final int alpha) - { - if (alpha == 0) - { - return; - } - - final float minX = Math.min(posA.getX(), posB.getX()); - final float minY = Math.min(posA.getY(), posB.getY()); - final float minZ = Math.min(posA.getZ(), posB.getZ()); - - final float maxX = Math.max(posA.getX(), posB.getX()) + 1; - final float maxY = Math.max(posA.getY(), posB.getY()) + 1; - final float maxZ = Math.max(posA.getZ(), posB.getZ()) + 1; - - populateCuboid(minX, minY, minZ, maxX, maxY, maxZ, red, green, blue, alpha, poseStack.last().pose(), bufferSource.getBuffer(renderType)); - } - - protected final void populateCuboid(final float minX, - final float minY, - final float minZ, - final float maxX, - final float maxY, - final float maxZ, - final int red, - final int green, - final int blue, - final int alpha, - final Matrix4f m, - final VertexConsumer buf) - { - // z plane - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - - // y plane - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - // x plane - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - } - - public final void renderFillRectangle(final int x, - final int y, - final int z, - final int w, - final int h, - final int argbColor) - { - populateRectangle(x, - y, - z, - w, - h, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - bufferSource.getBuffer(COLORED_TRIANGLES_NC_ND), - poseStack.last().pose()); - } - - protected final void populateRectangle(final int x, - final int y, - final int z, - final int w, - final int h, - final int red, - final int green, - final int blue, - final int alpha, - final VertexConsumer buffer, - final Matrix4f m) - { - if (alpha == 0) - { - return; - } - - buffer.addVertex(m, x, y, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x, y + h, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x + w, y + h, z).setColor(red, green, blue, alpha); - - buffer.addVertex(m, x, y, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x + w, y + h, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x + w, y, z).setColor(red, green, blue, alpha); - } - - /** - * Renders the given list of strings, 3 elements a row. - * - * @param pos position to render at - * @param text text list - * @param matrixStack stack to use - * @param buffer render buffer - * @param forceWhite force white for no depth rendering - * @param mergeEveryXListElements merge every X elements of text list using a tostring call - */ - public final void renderDebugText(final BlockPos pos, - final List text, - final boolean forceWhite, - final int mergeEveryXListElements) - { - renderDebugText(pos, pos, text, forceWhite, mergeEveryXListElements); - } - - /** - * Renders the given list of strings, 3 elements a row. - * - * @param renderPos position to render at - * @param worldPos (logic) position in world - * @param text text list - * @param matrixStack stack to use - * @param buffer render buffer - * @param forceWhite force white for no depth rendering - * @param mergeEveryXListElements merge every X elements of text list using a tostring call - */ - @SuppressWarnings("resource") - public final void renderDebugText(final BlockPos renderPos, - final BlockPos worldPos, - final List text, - final boolean forceWhite, - final int mergeEveryXListElements) - { - if (mergeEveryXListElements < 1) - { - throw new IllegalArgumentException("mergeEveryXListElements is less than 1"); - } - - final EntityRenderDispatcher erm = Minecraft.getInstance().getEntityRenderDispatcher(); - final int cap = text.size(); - if (cap > 0 && erm.distanceToSqr(worldPos.getX(), worldPos.getY(), worldPos.getZ()) <= MAX_DEBUG_TEXT_RENDER_DIST_SQUARED) - { - final Font fontrenderer = Minecraft.getInstance().font; - - poseStack.pushPose(); - poseStack.translate(renderPos.getX() + 0.5d, renderPos.getY() + 0.6d, renderPos.getZ() + 0.5d); - poseStack.mulPose(erm.cameraOrientation()); - poseStack.scale(0.014f, -0.014f, 0.014f); - - final float backgroundTextOpacity = Minecraft.getInstance().options.getBackgroundOpacity(0.25F); - final int alphaMask = (int) (backgroundTextOpacity * 255.0F) << 24; - - final Matrix4f rawPosMatrix = poseStack.last().pose(); - - for (int i = 0; i < cap; i += mergeEveryXListElements) - { - final MutableComponent renderText = Component.literal( - mergeEveryXListElements == 1 ? text.get(i) : text.subList(i, Math.min(i + mergeEveryXListElements, cap)).toString()); - final float textCenterShift = (float) (-fontrenderer.width(renderText) / 2); - - fontrenderer.drawInBatch(renderText, - textCenterShift, - 0, - forceWhite ? 0xffffffff : 0x20ffffff, - false, - rawPosMatrix, - bufferSource, - Font.DisplayMode.SEE_THROUGH, - alphaMask, - 0x00f000f0); - if (!forceWhite) - { - fontrenderer.drawInBatch(renderText, textCenterShift, 0, 0xffffffff, false, rawPosMatrix, bufferSource, Font.DisplayMode.NORMAL, 0, 0x00f000f0); - } - poseStack.translate(0.0d, fontrenderer.lineHeight + 1, 0.0d); - } - - poseStack.popPose(); - } - } - - public static final class RenderTypes extends RenderType - { - private RenderTypes(final String nameIn, - final VertexFormat formatIn, - final VertexFormat.Mode drawModeIn, - final int bufferSizeIn, - final boolean useDelegateIn, - final boolean needsSortingIn, - final Runnable setupTaskIn, - final Runnable clearTaskIn) - { - super(nameIn, formatIn, drawModeIn, bufferSizeIn, useDelegateIn, needsSortingIn, setupTaskIn, clearTaskIn); - throw new IllegalStateException(); - } - - private static final RenderType GLINT_LINES = create("structurize_glint_lines", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.DEBUG_LINES, - 1 << 12, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(GLINT_TRANSPARENCY) - .setDepthTestState(NeverDepthTestStateShard.NEVER_DEPTH_TEST) - .setCullState(NO_CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - private static final RenderType GLINT_LINES_WITH_WIDTH = create("structurize_glint_lines_with_width", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 13, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(GLINT_TRANSPARENCY) - .setDepthTestState(AlwaysDepthTestStateShard.ALWAYS_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_DEPTH_WRITE) - .createCompositeState(false)); - - private static final RenderType LINES = create("structurize_lines", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.DEBUG_LINES, - 1 << 14, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(LEQUAL_DEPTH_TEST) - .setCullState(NO_CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - private static final RenderType LINES_WITH_WIDTH = create("structurize_lines_with_width", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 13, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(LEQUAL_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_DEPTH_WRITE) - .createCompositeState(false)); - - private static final RenderType LINES_WITH_WIDTH_DEPTH_INVERT = create("structurize_lines_with_width_depth_invert", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 12, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(GREATER_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - private static final RenderType COLORED_TRIANGLES = create("structurize_colored_triangles", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 13, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(LEQUAL_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_DEPTH_WRITE) - .createCompositeState(false)); - - private static final RenderType COLORED_TRIANGLES_NC_ND = create("structurize_colored_triangles_nc_nd", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 12, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(NeverDepthTestStateShard.NEVER_DEPTH_TEST) - .setCullState(NO_CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - /** - * Register our buffers - */ - public static void registerBuffer(final RegisterRenderBuffersEvent event) - { - event.registerRenderBuffer(LINES); - event.registerRenderBuffer(LINES_WITH_WIDTH); - event.registerRenderBuffer(LINES_WITH_WIDTH_DEPTH_INVERT); - event.registerRenderBuffer(GLINT_LINES); - event.registerRenderBuffer(GLINT_LINES_WITH_WIDTH); - event.registerRenderBuffer(COLORED_TRIANGLES); - event.registerRenderBuffer(COLORED_TRIANGLES_NC_ND); - } - - /** - * Managed by structurize, ends above buffers in context similar to {@link RenderType#LINES} - */ - public static void finishBuffer(final RenderLevelStageEvent event) - { - final MultiBufferSource.BufferSource bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); - final Stage stage = event.getStage(); - - if (stage == Stage.AFTER_BLOCK_ENTITIES) - { - bufferSource.endBatch(LINES_WITH_WIDTH_DEPTH_INVERT); - - bufferSource.endBatch(COLORED_TRIANGLES); - bufferSource.endBatch(COLORED_TRIANGLES_NC_ND); - - bufferSource.endBatch(LINES); - bufferSource.endBatch(LINES_WITH_WIDTH); - - // fallthrough into levelRenderer master endBatch - // bufferSource.endBatch(GLINT_LINES); - // bufferSource.endBatch(GLINT_LINES_WITH_WIDTH); - } - } - - public static class NeverDepthTestStateShard extends DepthTestStateShard - { - public static final DepthTestStateShard NEVER_DEPTH_TEST = new NeverDepthTestStateShard(); - - private NeverDepthTestStateShard() - { - super("true_never", -1); - setupState = () -> { - RenderSystem.enableDepthTest(); - RenderSystem.depthFunc(GL30C.GL_NEVER); - }; - } - } - - public static class AlwaysDepthTestStateShard extends DepthTestStateShard - { - public static final DepthTestStateShard ALWAYS_DEPTH_TEST = new AlwaysDepthTestStateShard(); - - private AlwaysDepthTestStateShard() - { - super("true_always", -1); - setupState = () -> { - RenderSystem.enableDepthTest(); - RenderSystem.depthFunc(GL30C.GL_ALWAYS); - }; - } - } - } -} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index ac4a2aacbb..4a848d7174 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1,29 +1,27 @@ -public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix FLOWER_POT_MAP -public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix NOTE_BLOCK_MAP +public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix f_15048_ # FLOWER_POT_MAP +public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix f_15051_ # NOTE_BLOCK_MAP # blueprint renderer -public net.minecraft.client.Camera setPosition(Lnet/minecraft/world/phys/Vec3;)V -public-f net.minecraft.client.renderer.RenderStateShard setupState -public net.minecraft.client.renderer.culling.Frustum cubeInFrustum(DDDDDD)Z +public net.minecraft.client.Camera m_90581_(Lnet/minecraft/world/phys/Vec3;)V # setPostion ## blending -public com.mojang.blaze3d.platform.GlStateManager BLEND +public com.mojang.blaze3d.platform.GlStateManager BLEND # BLEND public com.mojang.blaze3d.platform.GlStateManager$BlendState public com.mojang.blaze3d.platform.GlStateManager$BooleanState -public com.mojang.blaze3d.platform.GlStateManager$BooleanState enabled +public com.mojang.blaze3d.platform.GlStateManager$BooleanState f_84586_ # enabled + +# DO upgrade from non DO world +public net.minecraft.server.level.ChunkMap m_140427_(Lnet/minecraft/world/level/ChunkPos;)Lnet/minecraft/nbt/CompoundTag; #readChunk +public net.minecraft.server.MinecraftServer f_129744_ #storageSource # world gen settings -public net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator createNoiseChunk(Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/levelgen/RandomState;)Lnet/minecraft/world/level/levelgen/NoiseChunk; +public net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator m_224256_(Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/levelgen/RandomState;)Lnet/minecraft/world/level/levelgen/NoiseChunk; # createNoiseChunk public net.minecraft.world.level.levelgen.SurfaceRules$Context -public net.minecraft.world.level.levelgen.SurfaceRules$Context (Lnet/minecraft/world/level/levelgen/SurfaceSystem;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/levelgen/NoiseChunk;Ljava/util/function/Function;Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/WorldGenerationContext;)V -public net.minecraft.world.level.levelgen.SurfaceRules$Context updateXZ(II)V -public net.minecraft.world.level.levelgen.SurfaceRules$Context updateY(IIIIII)V +public net.minecraft.world.level.levelgen.SurfaceRules$Context (Lnet/minecraft/world/level/levelgen/SurfaceSystem;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/levelgen/NoiseChunk;Ljava/util/function/Function;Lnet/minecraft/world/level/levelgen/WorldGenerationContext;Ljava/util/Set;)V +public net.minecraft.world.level.levelgen.SurfaceRules$Context updateXZ(II)V # updateXZ +public net.minecraft.world.level.levelgen.SurfaceRules$Context updateY(IIII)V # updateY public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule # keybinds -public net.minecraft.client.KeyMapping clickCount - -# itemHandler -public net.minecraft.world.entity.decoration.GlowItemFrame getFrameItemStack()Lnet/minecraft/world/item/ItemStack; -public net.minecraft.world.entity.decoration.ItemFrame getFrameItemStack()Lnet/minecraft/world/item/ItemStack; +public net.minecraft.client.KeyMapping f_90818_ # clickCount -public net.minecraft.world.level.block.state.BlockBehaviour hasCollision # hasCollision \ No newline at end of file +public net.minecraft.world.level.block.state.BlockBehaviour f_60443_ # hasCollision diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 797add3466..42e07bf7d3 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -1,10 +1,10 @@ modLoader="javafml" -loaderVersion="${fmlRange}" +loaderVersion="${fml_range}" license="GPL 3.0" issueTrackerURL="https://github.com/ldtteam/structurize/issues/new/choose" [[mods]] modId="structurize" -version="${version}" #mandatory +version="${file.jarVersion}" displayName="Structurize" displayURL="https://minecolonies.com/" logoFile="structurize.png" @@ -22,7 +22,7 @@ Allows you to: [[dependencies.structurize]] modId="neoforge" type="required" - versionRange="${forgeRange}" + versionRange="${neoforge_range}" ordering="NONE" side="BOTH" [[dependencies.structurize]] @@ -34,12 +34,12 @@ Allows you to: [[dependencies.structurize]] modId="blockui" type="required" - versionRange="[${blockUiVersion}, )" - ordering="AFTER" - side="BOTH" + versionRange="${blockUiRange}" + ordering="NONE" + side="CLIENT" [[dependencies.structurize]] modId="domum_ornamentum" type="required" - versionRange="[${domumOrnamentumVersion}, )" + versionRange="${domumOrnamentumRange}" ordering="AFTER" side="BOTH" diff --git a/src/main/resources/assets/structurize/gui/dialogconfirmtransparency.xml b/src/main/resources/assets/structurize/gui/dialogconfirmtransparency.xml deleted file mode 100644 index a1b64a7a5d..0000000000 --- a/src/main/resources/assets/structurize/gui/dialogconfirmtransparency.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - -