From 74c77f1de416154a54e1007ed7e3f6efb538c633 Mon Sep 17 00:00:00 2001 From: gniftygnome <50962022+gniftygnome@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:40:13 -0800 Subject: [PATCH] First pass at an update to 1.21.2. (#95) * First pass at an update to 1.21.2. - Make it build against 1.21.2 - Fix broken things - Replace basically all of the boat API * I forgot about PillarLogHelper... - PillarLogHelper.of methods removed - PillarLogHelper.settings methods added They provide AbstractBlock.Settings instead of PillarLog - Improvements to the testmod * Darn it IDEA, why do you keep removing this change from my commits? * Adjust name of new pillar log helpers. I was trying to keep them short, but I didn't really like the names either, so I'm fine with this. Co-authored-by: haykam821 <24855774+haykam821@users.noreply.github.com> * Update ancillary gradle cruft to match version 8.8. * Update to 1.21.2-rc1. Fix wood test. * Fix the raft entity renderers. * Add DFU support so old boats can become new boats. This is the nice clean way I wanted to do but can't because somebody decided the DFU should init before mods do... * Version which enables mods to register boat IDs early. - Provide a hook for mods to give us their boat IDs (so datafixer retains the boat type instead of using oak) This is the icky version I hate to have to make, but the DFU initializes way too early to do this in any nice way... --------- Co-authored-by: haykam821 <24855774+haykam821@users.noreply.github.com> --- build.gradle | 2 +- gradle.properties | 11 +- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 19 +-- gradlew.bat | 20 +-- .../dirt/api/block/TerraformGrassBlock.java | 9 +- .../dirt/impl/mixin/MixinEatGrassGoal.java | 3 +- .../registry/TillableBlockRegistryImpl.java | 4 +- .../terraform/boat/api/TerraformBoatType.java | 86 ----------- .../boat/api/TerraformBoatTypeRegistry.java | 33 ----- .../api/client/TerraformBoatClientHelper.java | 78 ++-------- .../boat/api/data/TerraformBoatDfuApi.java | 33 +++++ .../api/item/TerraformBoatItemHelper.java | 134 +++++++++-------- .../boat/impl/TerraformBoatInitializer.java | 39 ----- .../boat/impl/TerraformBoatTrackedData.java | 28 ---- .../boat/impl/TerraformBoatTypeImpl.java | 42 ------ .../client/TerraformBoatClientHelperImpl.java | 54 +++++++ .../TerraformBoatClientInitializer.java | 17 --- .../client/TerraformBoatEntityRenderer.java | 69 --------- .../impl/data/TerraformAddBoatsSchema.java | 26 ++++ .../boat/impl/data/TerraformBoatData.java | 138 ++++++++++++++++++ .../boat/impl/data/TerraformBoatDfu.java | 38 +++++ .../boat/impl/data/TerraformBoatSplitFix.java | 95 ++++++++++++ .../impl/data/TerraformBoatSplitSchema.java | 36 +++++ .../boat/impl/entity/TerraformBoatEntity.java | 111 -------------- .../boat/impl/entity/TerraformBoatHolder.java | 41 ------ .../impl/entity/TerraformChestBoatEntity.java | 112 -------------- .../item/TerraformBoatDispenserBehavior.java | 85 ----------- .../boat/impl/item/TerraformBoatItem.java | 110 -------------- .../item/TerraformBoatItemHelperImpl.java | 113 ++++++++++++++ .../boat/impl/mixin/MixinBoatEntity.java | 22 --- .../impl/mixin/MixinBoatEntityRenderer.java | 35 ----- .../boat/impl/mixin/MixinEntityPredicate.java | 31 ---- .../boat/impl/mixin/MixinSchemas.java | 69 +++++++++ .../leaves/api/block/ExtendedLeavesBlock.java | 11 +- .../leaves/api/block/LeafPileBlock.java | 7 +- .../api/block/TransparentLeavesBlock.java | 4 +- .../sign/impl/mixin/MixinSignEditScreen.java | 15 +- .../wood/api/block/BareSmallLogBlock.java | 2 +- .../wood/api/block/PillarLogHelper.java | 44 +++--- .../wood/api/block/SmallLogBlock.java | 2 +- .../src/main/resources/fabric.mod.json | 6 +- ...-boats.json => mixins.terraform-boat.json} | 6 +- ...-signs.json => mixins.terraform-sign.json} | 0 .../wood/test/TerraformWoodTest.java | 99 ++++++------- .../wood/test/TerraformWoodTestClient.java | 3 +- .../wood/test/command/SpawnBoatsCommand.java | 27 ++-- .../assets/terraform/lang/en_us.json | 50 +++++++ .../models/item/custom_chest_raft.json | 6 + .../terraform/models/item/custom_raft.json | 6 + .../boat/{custom_boat.png => custom.png} | Bin .../{custom_boat.png => custom.png} | Bin .../textures/entity/chest_raft/custom.png | Bin 0 -> 13724 bytes .../terraform/textures/entity/raft/custom.png | Bin 0 -> 10093 bytes .../textures/item/custom_chest_raft.png | Bin 0 -> 6680 bytes .../terraform/textures/item/custom_raft.png | Bin 0 -> 6629 bytes 57 files changed, 901 insertions(+), 1134 deletions(-) mode change 100644 => 100755 gradlew delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatType.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatTypeRegistry.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/data/TerraformBoatDfuApi.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatInitializer.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTrackedData.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTypeImpl.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientHelperImpl.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientInitializer.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatEntityRenderer.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformAddBoatsSchema.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatData.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatDfu.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitFix.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitSchema.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatEntity.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatHolder.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformChestBoatEntity.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatDispenserBehavior.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItem.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItemHelperImpl.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntity.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntityRenderer.java delete mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinEntityPredicate.java create mode 100644 terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinSchemas.java rename terraform-wood-api-v1/src/main/resources/{mixins.terraform-boats.json => mixins.terraform-boat.json} (65%) rename terraform-wood-api-v1/src/main/resources/{mixins.terraform-signs.json => mixins.terraform-sign.json} (100%) create mode 100644 terraform-wood-api-v1/src/testmod/resources/assets/terraform/lang/en_us.json create mode 100644 terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_chest_raft.json create mode 100644 terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_raft.json rename terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/boat/{custom_boat.png => custom.png} (100%) rename terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_boat/{custom_boat.png => custom.png} (100%) create mode 100644 terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_raft/custom.png create mode 100644 terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/raft/custom.png create mode 100644 terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/item/custom_chest_raft.png create mode 100644 terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/item/custom_raft.png diff --git a/build.gradle b/build.gradle index 3d6d9dbf..f90eff32 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'maven-publish' id 'idea' id 'eclipse' - id 'fabric-loom' version '1.6.+' + id 'fabric-loom' version '1.7.+' } allprojects { diff --git a/gradle.properties b/gradle.properties index 94391f2b..6192c138 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,11 +1,10 @@ org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true -fabric.loom.multiProjectOptimisation=true maven_group=com.terraformersmc.terraform-api -version=11.0.0-alpha.1 +version=12.0.0-alpha.1 -minecraft_version=1.21 -yarn_mappings=1.21+build.7 -loader_version=0.15.11 -fabric_version=0.100.4+1.21 +minecraft_version=1.21.2 +yarn_mappings=1.21.2+build.1 +loader_version=0.16.7 +fabric_version=0.106.1+1.21.2 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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≫L+crb^Bz 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 /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * 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. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f..25da30db 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/api/block/TerraformGrassBlock.java b/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/api/block/TerraformGrassBlock.java index 205ad467..906d1bcb 100644 --- a/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/api/block/TerraformGrassBlock.java +++ b/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/api/block/TerraformGrassBlock.java @@ -23,6 +23,7 @@ public class TerraformGrassBlock extends GrassBlock { private final Map spreadsTo; private final Supplier path; public static final Map GRASS_SPREADS_TO = new HashMap<>(); + private static final int MAX_LIGHT_LEVEL = 15; public TerraformGrassBlock(Block dirt, Supplier path, Block.Settings settings) { this(dirt, path, settings, ImmutableMap.of(Blocks.DIRT, Blocks.GRASS_BLOCK)); @@ -55,11 +56,13 @@ private static boolean canSurvive(BlockState state, WorldView world, BlockPos po BlockPos above = pos.up(); BlockState aboveState = world.getBlockState(above); - if (aboveState.getBlock() == Blocks.SNOW && aboveState.get(SnowBlock.LAYERS) == 1) { + if (aboveState.isOf(Blocks.SNOW) && aboveState.get(SnowBlock.LAYERS) == 1) { return true; + } else if (aboveState.getFluidState().getLevel() == 8) { + return false; } else { - int lightingAt = ChunkLightProvider.getRealisticOpacity(world, state, pos, aboveState, above, Direction.UP, aboveState.getOpacity(world, above)); - return lightingAt < world.getMaxLightLevel(); + int lightingAt = ChunkLightProvider.getRealisticOpacity(state, aboveState, Direction.UP, aboveState.getOpacity()); + return lightingAt < MAX_LIGHT_LEVEL; } } diff --git a/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/mixin/MixinEatGrassGoal.java b/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/mixin/MixinEatGrassGoal.java index 96ab4463..43aebc79 100644 --- a/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/mixin/MixinEatGrassGoal.java +++ b/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/mixin/MixinEatGrassGoal.java @@ -8,6 +8,7 @@ import net.minecraft.block.Blocks; import net.minecraft.entity.ai.goal.EatGrassGoal; import net.minecraft.entity.mob.MobEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.world.GameRules; import net.minecraft.world.World; @@ -52,7 +53,7 @@ public class MixinEatGrassGoal { BlockState down = this.world.getBlockState(downPos); if (down.isIn(TerraformDirtBlockTags.GRASS_BLOCKS)) { - if (this.world.getGameRules().getBoolean(GameRules.DO_MOB_GRIEFING)) { + if (((ServerWorld) this.world).getGameRules().getBoolean(GameRules.DO_MOB_GRIEFING)) { this.world.syncWorldEvent(2001, downPos, Block.getRawIdFromState(Blocks.GRASS_BLOCK.getDefaultState())); Block replacement = TerraformDirtRegistryImpl.getByGrassBlock(down.getBlock()).map(DirtBlocks::getDirt).orElse(Blocks.DIRT); diff --git a/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/registry/TillableBlockRegistryImpl.java b/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/registry/TillableBlockRegistryImpl.java index 9e908070..48a2424c 100644 --- a/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/registry/TillableBlockRegistryImpl.java +++ b/terraform-dirt-api-v1/src/main/java/com/terraformersmc/terraform/dirt/impl/registry/TillableBlockRegistryImpl.java @@ -10,8 +10,8 @@ import java.util.function.Predicate; public final class TillableBlockRegistryImpl extends HoeItem { - private TillableBlockRegistryImpl(ToolMaterial material, Settings settings) { - super(material, settings); + private TillableBlockRegistryImpl(ToolMaterial material, float attackDamage, float attackSpeed, Settings settings) { + super(material, attackDamage, attackSpeed, settings); return; } diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatType.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatType.java deleted file mode 100644 index 14ccd48f..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatType.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.terraformersmc.terraform.boat.api; - -import com.terraformersmc.terraform.boat.impl.TerraformBoatTypeImpl; -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatEntity; -import com.terraformersmc.terraform.boat.impl.entity.TerraformChestBoatEntity; - -import net.minecraft.item.Item; - -/** - * An interface representing a Terraform boat. - */ -public interface TerraformBoatType { - /** - * {@return whether this boat is a raft with a lower {@linkplain net.minecraft.entity.vehicle.BoatEntity#getMountedHeightOffset() mounted height offset}} - */ - boolean isRaft(); - - /** - * {@return the {@linkplain net.minecraft.entity.vehicle.BoatEntity#getPickBlockStack() pick stack} and {@linkplain Item item} dropped when the {@linkplain TerraformBoatEntity boat entity} is broken} - */ - Item getItem(); - - /** - * {@return the {@linkplain net.minecraft.entity.vehicle.BoatEntity#getPickBlockStack() pick stack} and {@linkplain Item item} dropped when the {@linkplain TerraformChestBoatEntity chest boat entity} is broken} - */ - Item getChestItem(); - - /** - * {@return the planks {@linkplain Item item} dropped when the {@linkplain TerraformBoatEntity boat entity} or {@linkplain TerraformChestBoatEntity chest boat entity} is destroyed into planks and sticks} - */ - Item getPlanks(); - - /** - * A builder for {@linkplain TerraformBoatType Terraform boat types}. - * - *

To build a Terraform boat type: - * - *

{@code
-	 *     TerraformBoatType boat = new TerraformBoatType.Builder()
-	 *         .item(ExampleModItems.MAHOGANY_BOAT)
-	 *         .build();
-	 * }
- */ - public static class Builder { - private boolean raft; - private Item item; - private Item chestItem; - private Item planks; - - public TerraformBoatType build() { - return new TerraformBoatTypeImpl(this.raft, this.item, this.chestItem, this.planks); - } - - /** - * @see TerraformBoatType#isRaft - */ - public Builder raft() { - this.raft = true; - return this; - } - - /** - * @see TerraformBoatType#getItem - */ - public Builder item(Item item) { - this.item = item; - return this; - } - - /** - * @see TerraformBoatType#getChestItem - */ - public Builder chestItem(Item chestItem) { - this.chestItem = chestItem; - return this; - } - - /** - * @see TerraformBoatType#getPlanks - */ - public Builder planks(Item planks) { - this.planks = planks; - return this; - } - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatTypeRegistry.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatTypeRegistry.java deleted file mode 100644 index 5ecfcd53..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/TerraformBoatTypeRegistry.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.terraformersmc.terraform.boat.api; - -import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.util.Identifier; - -/** - * @see TerraformBoatTypeRegistry#INSTANCE - */ -public class TerraformBoatTypeRegistry { - private static final Identifier REGISTRY_ID = Identifier.of("terraform", "boat"); - private static final RegistryKey> REGISTRY_KEY = RegistryKey.ofRegistry(REGISTRY_ID); - - /** - * The registry for {@linkplain TerraformBoatType Terraform boats}. - * - * - *

To register a boat type: - * - *

{@code
-	 *     Registry.register(TerraformBoatType.REGISTRY, Identifier.of("examplemod", "mahogany"), boat);
-	 * }
- * - * @see com.terraformersmc.terraform.boat.api.TerraformBoatType.Builder The builder for boat types - * @see com.terraformersmc.terraform.boat.api.client.TerraformBoatClientHelper Helpers for registering the boat on the client - */ - public static final Registry INSTANCE = FabricRegistryBuilder.createSimple(REGISTRY_KEY).buildAndRegister(); - - public static RegistryKey createKey(Identifier id) { - return RegistryKey.of(REGISTRY_KEY, id); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/client/TerraformBoatClientHelper.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/client/TerraformBoatClientHelper.java index 9ae5f7ca..e48bd6d6 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/client/TerraformBoatClientHelper.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/client/TerraformBoatClientHelper.java @@ -1,83 +1,35 @@ package com.terraformersmc.terraform.boat.api.client; +import com.terraformersmc.terraform.boat.impl.client.TerraformBoatClientHelperImpl; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; -import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry.TexturedModelDataProvider; -import net.minecraft.client.render.entity.model.BoatEntityModel; -import net.minecraft.client.render.entity.model.ChestBoatEntityModel; -import net.minecraft.client.render.entity.model.ChestRaftEntityModel; +import net.minecraft.client.render.entity.BoatEntityRenderer; import net.minecraft.client.render.entity.model.EntityModelLayer; -import net.minecraft.client.render.entity.model.RaftEntityModel; import net.minecraft.util.Identifier; -@Environment(EnvType.CLIENT) /** - * This class provides useful helpers for registering a {@linkplain com.terraformersmc.terraform.boat.api.TerraformBoatType Terraform boat} on the client. + * This class provides useful helpers for registering a {@linkplain net.minecraft.entity.vehicle.BoatEntity boat} on the client. */ +@Environment(EnvType.CLIENT) +@SuppressWarnings("unused") public final class TerraformBoatClientHelper { private TerraformBoatClientHelper() { return; } /** - * Gets the identifier of a {@linkplain EntityModelLayer model layer} for a boat of a given type. - * @param boatId the {@linkplain net.minecraft.util.Identifier identifier} of the {@linkplain com.terraformersmc.terraform.boat.api.TerraformBoatType boat} - * @param raft whether the boat is a raft - * @param chest whether the boat contains a chest - */ - private static Identifier getLayerId(Identifier boatId, boolean raft, boolean chest) { - String prefix = raft ? (chest ? "chest_raft/" : "raft/") : (chest ? "chest_boat/" : "boat/"); - return boatId.withPrefixedPath(prefix); - } - - /** - * Creates a {@linkplain EntityModelLayer model layer} for a boat of a given type. - * @param boatId the {@linkplain net.minecraft.util.Identifier identifier} of the {@linkplain com.terraformersmc.terraform.boat.api.TerraformBoatType boat} - * @param raft whether the boat is a raft - * @param chest whether the boat contains a chest - * - *
{@code
-	 *     EntityModelLayer layer = TerraformBoatClientHelper.getLayer(Identifier.of("examplemod", "mahogany"), false, false);
-	 * }
- */ - public static EntityModelLayer getLayer(Identifier boatId, boolean raft, boolean chest) { - return new EntityModelLayer(getLayerId(boatId, raft, chest), "main"); - } - - private static TexturedModelDataProvider getTexturedModelDataProvider(boolean raft, boolean chest) { - if (raft) { - return chest ? ChestRaftEntityModel::getTexturedModelData : RaftEntityModel::getTexturedModelData; - } else { - return chest ? ChestBoatEntityModel::getTexturedModelData : BoatEntityModel::getTexturedModelData; - } - } - - /** - * Registers a {@linkplain EntityModelLayer model layer} for a boat of a given type. - * @param boatId the {@linkplain net.minecraft.util.Identifier identifier} of the {@linkplain com.terraformersmc.terraform.boat.api.TerraformBoatType boat} - * @param raft whether the boat is a raft - * @param chest whether the boat contains a chest - * - *
{@code
-	 *     TerraformBoatClientHelper.registerModelLayer(Identifier.of("examplemod", "mahogany"), false, false);
-	 * }
- */ - private static void registerModelLayer(Identifier boatId, boolean raft, boolean chest) { - EntityModelLayerRegistry.registerModelLayer(getLayer(boatId, raft, chest), getTexturedModelDataProvider(raft, chest)); - } - - /** - * Registers {@linkplain EntityModelLayer model layers} for a given boat type. - * @param boatId the {@linkplain net.minecraft.util.Identifier identifier} of the {@linkplain com.terraformersmc.terraform.boat.api.TerraformBoatType boat type} - * @param raft whether the boat is a raft - * + * Registers {@linkplain EntityModelLayer model layers} and + * {@linkplain BoatEntityRenderer entity renderers} for all boats of given boat type. + * The provided identifier must match the identifier used to + * {@linkplain com.terraformersmc.terraform.boat.api.item.TerraformBoatItemHelper#registerBoatItem register the boat type}. + * *
{@code
-	 *     TerraformBoatClientHelper.registerModelLayers(Identifier.of("examplemod", "mahogany"), false);
+	 *     TerraformBoatClientHelper.registerModelLayers(Identifier.of("examplemod", "mahogany"));
 	 * }
+ * + * @param id the {@linkplain net.minecraft.util.Identifier identifier} of the boat type. */ - public static void registerModelLayers(Identifier boatId, boolean raft) { - registerModelLayer(boatId, raft, false); - registerModelLayer(boatId, raft, true); + public static void registerModelLayers(Identifier id) { + TerraformBoatClientHelperImpl.registerModelLayers(id); } } diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/data/TerraformBoatDfuApi.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/data/TerraformBoatDfuApi.java new file mode 100644 index 00000000..1963d2bd --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/data/TerraformBoatDfuApi.java @@ -0,0 +1,33 @@ +package com.terraformersmc.terraform.boat.api.data; + +import java.util.Collection; + +public interface TerraformBoatDfuApi { + + /** + * The purpose of this API is to allow pre-1.21.2 Terraform API boats to be converted to 1.21.2 and above. + * Properly registered boats which are submitted to this API will retain their existing wood type. Any + * Terraform API boats which are not submitted to this API will be upgraded to oak boat types instead. + * + * The TerraformBoatDfuApi.getDfuBoatIds() method will be called by the boat DFU code early during + * Minecraft start-up. It must be safe to call before the implementing mod has been initialized. When + * TerraformBoatDfuApi.getDfuBoatIds() is called it should return a collection of string-form identifiers + * -- f.e. {@code Set.of("yourmod:mahogany_boat", "yourmod:mahogany_chest_boat")} -- for all modded boats + * which should be tracked by the DFU. Identifiers must be post-migration (not, f.e., "terraform:boat" + * or "yourmod:mahogany"). Any boats listed must be registered during mod initialization. + * + * To use the boat DFU API, something similar to the following must be added to fabric.mod.json + * (where com.something.yourmod.init.TerraformBoatDfu is the class implementing TerraformBoatDfuApi): + * + *
{@code
+	 * "entrypoints": {
+	 *   "terraform-boat-dfu": [
+	 *     "com.something.yourmod.init.TerraformBoatDfu"
+	 *   ]
+	 * }
+	 * }
+ * + * @return Collection of string values of boat type identifiers + */ + Collection getDfuBoatIds(); +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/item/TerraformBoatItemHelper.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/item/TerraformBoatItemHelper.java index 9cd8f0a2..be89dace 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/item/TerraformBoatItemHelper.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/api/item/TerraformBoatItemHelper.java @@ -1,91 +1,101 @@ package com.terraformersmc.terraform.boat.api.item; -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.impl.item.TerraformBoatDispenserBehavior; -import com.terraformersmc.terraform.boat.impl.item.TerraformBoatItem; - -import net.minecraft.block.DispenserBlock; +import com.terraformersmc.terraform.boat.impl.item.TerraformBoatItemHelperImpl; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.vehicle.AbstractBoatEntity; +import net.minecraft.item.BoatItem; import net.minecraft.item.Item; import net.minecraft.item.ItemConvertible; -import net.minecraft.registry.Registries; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; import net.minecraft.util.Identifier; -/** - * This class provides utilities for the {@linkplain TerraformBoatItem item forms} of {@linkplain TerraformBoatType Terraform boats}, - * such as {@linkplain #registerBoatItem(Identifier, RegistryKey, boolean, Item.Settings) registering them and their dispenser behavior}. - */ +@SuppressWarnings("unused") public final class TerraformBoatItemHelper { private TerraformBoatItemHelper() { return; } /** - * Registers a {@linkplain TerraformBoatItem boat item} - * and its corresponding {@link #registerBoatDispenserBehavior dispenser behavior}. - * - *

To register a boat item and its dispenser behavior: - * + * Creates and registers a {@linkplain BoatItem boat item} and associated + * {@linkplain net.minecraft.entity.vehicle.BoatEntity boat entity} of the requested type + * and with the default {@linkplain Item.Settings}. This method assumes the type is a boat + * instead of a raft. + * + * This method should be called once for each boat type. + * Created items and entities will have identifiers similar to + * {@code id.withSuffixedPath("_boat")} and {@code id.withPrefixedPath("chest_raft/")}. + * *

{@code
-	 *     TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany_boat"), MAHOGANY_BOAT_KEY, false);
+	 *     BoatItem boat = TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany"), false);
+	 *     BoatItem chestBoat = TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany"), true);
 	 * }
- * - *

This method should be called twice for a given boat type for both boats and chest boats. - * - *

This method does not define item groups for the item. - * - * @see #registerBoatItem(Identifier, RegistryKey, boolean, Item.Settings) Helper that allows specifying a custom item settings - * - * @param id the {@linkplain Identifier identifier} to register the item with - * @param boatKey a {@linkplain RegistryKey registry key} for the {@linkplain TerraformBoatType Terraform boat type} that should be spawned by this item and dispenser behavior - * @param chest whether the boat contains a chest + * + * @param id The identifier of the boat family + * @param chest Whether the boat is a chest boat + * @return The created, registered boat item */ - public static Item registerBoatItem(Identifier id, RegistryKey boatKey, boolean chest) { - return registerBoatItem(id, boatKey, chest, new Item.Settings().maxCount(1)); + public static BoatItem registerBoatItem(Identifier id, boolean chest) { + return registerBoatItem(id, chest, false); } /** - * Registers a {@linkplain TerraformBoatItem boat item} and its corresponding {@link #registerBoatDispenserBehavior dispenser behavior}. - * - *

To register a boat item and its dispenser behavior: - * + * Creates and registers a {@linkplain BoatItem boat item} and associated + * {@linkplain net.minecraft.entity.vehicle.BoatEntity boat entity} of the requested type + * and with the default {@linkplain Item.Settings}. + * + * This method should be called once for each boat type. Both boat and raft may be registered + * for the same wood type, if desired. Created items and entities will have identifiers similar to + * {@code id.withSuffixedPath("_boat")} and {@code id.withPrefixedPath("chest_raft/")}. + * *

{@code
-	 *     TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany_boat"), MAHOGANY_BOAT_KEY, false, new Item.Settings().maxCount(1));
+	 *     BoatItem boat = TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany"), false, false);
+	 *     BoatItem chestBoat = TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany"), true, false);
 	 * }
- * - *

This method should be called twice for a given boat type for both boats and chest boats. - * - *

This method does not define item groups for the item. - * - * @param id the {@linkplain Identifier identifier} to register the item with - * @param boatKey a {@linkplain RegistryKey registry key} for the {@linkplain TerraformBoatType Terraform boat type} that should be spawned by this item and dispenser behavior - * @param chest whether the boat contains a chest + * + * @param id The identifier of the boat family + * @param chest Whether the boat is a chest boat + * @param raft Whether the boat is a raft + * @return The created, registered boat item */ - public static Item registerBoatItem(Identifier id, RegistryKey boatKey, boolean chest, Item.Settings settings) { - Item item = new TerraformBoatItem(boatKey, chest, settings); - Registry.register(Registries.ITEM, id, item); - - registerBoatDispenserBehavior(item, boatKey, chest); - return item; + public static BoatItem registerBoatItem(Identifier id, boolean chest, boolean raft) { + return registerBoatItem(id, new Item.Settings().maxCount(1), chest, raft); } /** - * Registers a {@linkplain net.minecraft.block.dispenser.DispenserBehavior dispenser behavior} that spawns a {@linkplain com.terraformersmc.terraform.boat.impl.entity.TerraformBoatEntity boat entity} with a given {@linkplain TerraformBoatType Terraform boat type}. - * - *

To register a boat dispenser behavior: - * + * Creates and registers a {@linkplain BoatItem boat item} and associated + * {@linkplain net.minecraft.entity.vehicle.BoatEntity boat entity} of the requested type + * and with the provided {@linkplain Item.Settings}. + * + * This method should be called once for each boat type. Both boat and raft may be registered + * for the same wood type, if desired. Created items and entities will have identifiers similar to + * {@code id.withSuffixedPath("_boat")} and {@code id.withPrefixedPath("chest_raft/")}. + * *

{@code
-	 *     TerraformBoatItemHelper.registerBoatDispenserBehavior(MAHOGANY_BOAT_ITEM, MAHOGANY_BOAT_KEY, false);
+	 *     BoatItem boat = TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany"), settings, false, false);
+	 *     BoatItem chestBoat = TerraformBoatItemHelper.registerBoatItem(Identifier.of("examplemod", "mahogany"), settings, true, false);
 	 * }
- * - *

This method should be called twice for a given boat type for both boats and chest boats. - * - * @param item the item that should be assigned to this dispenser behavior - * @param boatKey a {@linkplain RegistryKey registry key} for the {@linkplain TerraformBoatType Terraform boat type} that should be spawned by this dispenser behavior - * @param chest whether the boat contains a chest + * + * @param id The identifier of the boat family + * @param settings Non-default item settings (f.e. changing stack size) + * @param chest Whether the boat is a chest boat + * @param raft Whether the boat is a raft + * @return The created, registered boat item + */ + public static BoatItem registerBoatItem(Identifier id, Item.Settings settings, boolean chest, boolean raft) { + return TerraformBoatItemHelperImpl.registerBoatItem(id, settings, chest, raft); + } + + /** + * Registers a vanilla {@link net.minecraft.block.dispenser.BoatDispenserBehavior boat dispenser behavior} + * for the provided {@linkplain ItemConvertible item} and + * {@linkplain net.minecraft.entity.vehicle.BoatEntity boat entity}. + * + * This registration is performed automatically by the {@linkplain TerraformBoatItemHelper#registerBoatItem} + * methods of this class. + * + * @param item The item for which to register the dispenser behavior + * @param boatEntity The boat entity which should be dispensed */ - public static void registerBoatDispenserBehavior(ItemConvertible item, RegistryKey boatKey, boolean chest) { - DispenserBlock.registerBehavior(item, new TerraformBoatDispenserBehavior(boatKey, chest)); + public static void registerBoatDispenserBehavior(ItemConvertible item, EntityType boatEntity) { + TerraformBoatItemHelperImpl.registerBoatDispenserBehavior(item, boatEntity); } } diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatInitializer.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatInitializer.java deleted file mode 100644 index 92f7dfd2..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatInitializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.terraformersmc.terraform.boat.impl; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatEntity; -import com.terraformersmc.terraform.boat.impl.entity.TerraformChestBoatEntity; - -import net.fabricmc.api.ModInitializer; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.SpawnGroup; -import net.minecraft.registry.Registries; -import net.minecraft.registry.Registry; -import net.minecraft.util.Identifier; - -public final class TerraformBoatInitializer implements ModInitializer { - private static final float DIMENSIONS_WIDTH = 1.375f; - private static final float DIMENSIONS_HEIGHT = 0.5625f; - - // Hack that prevents the following crash during client startup: - // Caused by: java.lang.NoClassDefFoundError: Could not initialize class com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry - private static final Registry registryInstance = TerraformBoatTypeRegistry.INSTANCE; - - private static final Identifier BOAT_ID = Identifier.of("terraform", "boat"); - public static final EntityType BOAT = EntityType.Builder.create(TerraformBoatEntity::new, SpawnGroup.MISC) - .dimensions(DIMENSIONS_WIDTH, DIMENSIONS_HEIGHT) - .build(); - - private static final Identifier CHEST_BOAT_ID = Identifier.of("terraform", "chest_boat"); - public static final EntityType CHEST_BOAT = EntityType.Builder.create(TerraformChestBoatEntity::new, SpawnGroup.MISC) - .dimensions(DIMENSIONS_WIDTH, DIMENSIONS_HEIGHT) - .build(); - - @Override - public void onInitialize() { - TerraformBoatTrackedData.register(); - Registry.register(Registries.ENTITY_TYPE, BOAT_ID, BOAT); - Registry.register(Registries.ENTITY_TYPE, CHEST_BOAT_ID, CHEST_BOAT); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTrackedData.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTrackedData.java deleted file mode 100644 index ce1f2512..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTrackedData.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.terraformersmc.terraform.boat.impl; - -import java.util.Optional; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; - -import net.minecraft.entity.data.TrackedDataHandler; -import net.minecraft.entity.data.TrackedDataHandlerRegistry; -import net.minecraft.network.RegistryByteBuf; -import net.minecraft.network.codec.PacketCodec; -import net.minecraft.network.codec.PacketCodecs; - -public final class TerraformBoatTrackedData { - private TerraformBoatTrackedData() { - return; - } - - public static final PacketCodec> PACKET_CODEC = PacketCodecs - .registryValue(TerraformBoatTypeRegistry.INSTANCE.getKey()) - .collect(PacketCodecs::optional); - - public static final TrackedDataHandler> HANDLER = TrackedDataHandler.create(PACKET_CODEC); - - protected static void register() { - TrackedDataHandlerRegistry.register(HANDLER); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTypeImpl.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTypeImpl.java deleted file mode 100644 index 92bb2562..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/TerraformBoatTypeImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.terraformersmc.terraform.boat.impl; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; - -import net.minecraft.item.Item; - -/** - * A simple implementation of {@link TerraformBoatType}. - */ -public class TerraformBoatTypeImpl implements TerraformBoatType { - private final boolean raft; - private final Item item; - private final Item chestItem; - private final Item planks; - - public TerraformBoatTypeImpl(boolean raft, Item item, Item chestItem, Item planks) { - this.raft = raft; - this.item = item; - this.chestItem = chestItem; - this.planks = planks; - } - - @Override - public boolean isRaft() { - return this.raft; - } - - @Override - public Item getItem() { - return this.item; - } - - @Override - public Item getChestItem() { - return this.chestItem; - } - - @Override - public Item getPlanks() { - return this.planks; - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientHelperImpl.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientHelperImpl.java new file mode 100644 index 00000000..8280ad36 --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientHelperImpl.java @@ -0,0 +1,54 @@ +package com.terraformersmc.terraform.boat.impl.client; + +import com.terraformersmc.terraform.boat.impl.data.TerraformBoatData; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry.TexturedModelDataProvider; +import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; +import net.minecraft.client.render.entity.BoatEntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.render.entity.RaftEntityRenderer; +import net.minecraft.client.render.entity.model.BoatEntityModel; +import net.minecraft.client.render.entity.model.EntityModelLayer; +import net.minecraft.client.render.entity.model.RaftEntityModel; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.util.Identifier; + +@Environment(EnvType.CLIENT) +public final class TerraformBoatClientHelperImpl { + private TerraformBoatClientHelperImpl() { + return; + } + + private static void registerEntityRenderer(EntityType entityType, EntityModelLayer modelLayer, TexturedModelDataProvider texturedModelDataProvider, EntityRendererFactory entityRendererFactory) { + EntityModelLayerRegistry.registerModelLayer(modelLayer, texturedModelDataProvider); + EntityRendererRegistry.register(entityType, entityRendererFactory); + } + + public static void registerModelLayers(Identifier id) { + TerraformBoatData boatData = TerraformBoatData.get(id); + + if (boatData.boatEntity() != null) { + registerEntityRenderer(boatData.boatEntity(), boatData.boatModelLayer(), + BoatEntityModel::getTexturedModelData, + context -> new BoatEntityRenderer(context, boatData.boatModelLayer())); + } + if (boatData.chestBoatEntity() != null) { + registerEntityRenderer(boatData.chestBoatEntity(), boatData.chestBoatModelLayer(), + BoatEntityModel::getChestTexturedModelData, + context -> new BoatEntityRenderer(context, boatData.chestBoatModelLayer())); + } + if (boatData.raftEntity() != null) { + registerEntityRenderer(boatData.raftEntity(), boatData.raftModelLayer(), + RaftEntityModel::getTexturedModelData, + context -> new RaftEntityRenderer(context, boatData.raftModelLayer())); + } + if (boatData.chestRaftEntity() != null) { + registerEntityRenderer(boatData.chestRaftEntity(), boatData.chestRaftModelLayer(), + RaftEntityModel::getChestTexturedModelData, + context -> new RaftEntityRenderer(context, boatData.chestRaftModelLayer())); + } + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientInitializer.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientInitializer.java deleted file mode 100644 index ff9a3572..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatClientInitializer.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.client; - -import com.terraformersmc.terraform.boat.impl.TerraformBoatInitializer; - -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; - -@Environment(EnvType.CLIENT) -public final class TerraformBoatClientInitializer implements ClientModInitializer { - @Override - public void onInitializeClient() { - EntityRendererRegistry.register(TerraformBoatInitializer.BOAT, context -> new TerraformBoatEntityRenderer(context, false)); - EntityRendererRegistry.register(TerraformBoatInitializer.CHEST_BOAT, context -> new TerraformBoatEntityRenderer(context, true)); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatEntityRenderer.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatEntityRenderer.java deleted file mode 100644 index be8691b2..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/client/TerraformBoatEntityRenderer.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.client; - -import java.util.Map; - -import com.google.common.collect.ImmutableMap; -import com.mojang.datafixers.util.Pair; -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; -import com.terraformersmc.terraform.boat.api.client.TerraformBoatClientHelper; -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatHolder; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.model.ModelPart; -import net.minecraft.client.render.entity.BoatEntityRenderer; -import net.minecraft.client.render.entity.EntityRendererFactory; -import net.minecraft.client.render.entity.model.BoatEntityModel; -import net.minecraft.client.render.entity.model.ChestBoatEntityModel; -import net.minecraft.client.render.entity.model.ChestRaftEntityModel; -import net.minecraft.client.render.entity.model.CompositeEntityModel; -import net.minecraft.client.render.entity.model.EntityModelLayer; -import net.minecraft.client.render.entity.model.RaftEntityModel; -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.util.Identifier; - -@Environment(EnvType.CLIENT) -public class TerraformBoatEntityRenderer extends BoatEntityRenderer { - private final Map>> texturesAndModels; - - public TerraformBoatEntityRenderer(EntityRendererFactory.Context context, boolean chest) { - super(context, chest); - - this.texturesAndModels = TerraformBoatTypeRegistry.INSTANCE.getEntrySet().stream().collect(ImmutableMap.toImmutableMap(entry -> { - return entry.getValue(); - }, entry -> { - boolean raft = entry.getValue().isRaft(); - String prefix = raft ? (chest ? "chest_raft/" : "raft/") : (chest ? "chest_boat/" : "boat/"); - - Identifier id = entry.getKey().getValue(); - Identifier textureId = id.withPath(path -> "textures/entity/" + prefix + path + ".png"); - - EntityModelLayer layer = TerraformBoatClientHelper.getLayer(id, raft, chest); - CompositeEntityModel model = createModel(context.getPart(layer), raft, chest); - - return new Pair<>(textureId, model); - })); - } - - @Override - public Identifier getTexture(BoatEntity entity) { - if (entity instanceof TerraformBoatHolder) { - TerraformBoatType boat = ((TerraformBoatHolder) entity).getTerraformBoat(); - return this.texturesAndModels.get(boat).getFirst(); - } - return super.getTexture(entity); - } - - public Pair> getTextureAndModel(TerraformBoatHolder holder) { - return this.texturesAndModels.get(holder.getTerraformBoat()); - } - - private CompositeEntityModel createModel(ModelPart part, boolean raft, boolean chest) { - if (raft) { - return chest ? new ChestRaftEntityModel(part) : new RaftEntityModel(part); - } else { - return chest ? new ChestBoatEntityModel(part) : new BoatEntityModel(part); - } - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformAddBoatsSchema.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformAddBoatsSchema.java new file mode 100644 index 00000000..f2f40119 --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformAddBoatsSchema.java @@ -0,0 +1,26 @@ +package com.terraformersmc.terraform.boat.impl.data; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; + +import java.util.Map; +import java.util.function.Supplier; + +public class TerraformAddBoatsSchema extends IdentifierNormalizingSchema { + public TerraformAddBoatsSchema(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public Map> registerEntities(Schema schema) { + Map> map = super.registerEntities(schema); + + schema.registerSimple(map, "terraform:boat"); + schema.register(map, "terraform:chest_boat", string -> DSL.optionalFields("Items", DSL.list(TypeReferences.ITEM_STACK.in(schema)))); + + return map; + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatData.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatData.java new file mode 100644 index 00000000..d113c8af --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatData.java @@ -0,0 +1,138 @@ +package com.terraformersmc.terraform.boat.impl.data; + +import net.minecraft.client.render.entity.model.EntityModelLayer; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.vehicle.*; +import net.minecraft.util.Identifier; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This internal implementation class provides data records used by + * {@linkplain com.terraformersmc.terraform.boat.api.client.TerraformBoatClientHelper Terraform boat helper} + * to complete client-side registration of the entities, models, and layers. + */ +public record TerraformBoatData(Identifier id, EntityType boatEntity, EntityType chestBoatEntity, EntityType raftEntity, EntityType chestRaftEntity) { + private static final Map BOAT_DATA = new ConcurrentHashMap<>(); + + public TerraformBoatData { + Objects.requireNonNull(id); + } + + + public static void put(TerraformBoatData boatData) { + BOAT_DATA.put(boatData.id, boatData); + } + + public static TerraformBoatData get(Identifier id) { + Objects.requireNonNull(id); + if (!BOAT_DATA.containsKey(id)) { + throw new RuntimeException("Request for unregistered boat data: " + id); + } + + return BOAT_DATA.get(id); + } + + public static Optional getOptional(Identifier id) { + if (id == null || !BOAT_DATA.containsKey(id)) { + return Optional.empty(); + } + + return Optional.of(BOAT_DATA.get(id)); + } + + + public static TerraformBoatData empty(Identifier id) { + return new TerraformBoatData(id, null, null, null, null); + } + + public static void addBoat(Identifier id, EntityType boatEntity) { + if (BOAT_DATA.containsKey(id)) { + TerraformBoatData old = BOAT_DATA.get(id); + if (old.boatEntity != null) { + throw new IllegalStateException("Attempted to replace existing boat entity: " + old.boatId()); + } + put(new TerraformBoatData(id, boatEntity, old.chestBoatEntity, old.raftEntity, old.chestRaftEntity)); + } else { + put(new TerraformBoatData(id, boatEntity, null, null, null)); + } + } + + public static void addChestBoat(Identifier id, EntityType chestBoatEntity) { + if (BOAT_DATA.containsKey(id)) { + TerraformBoatData old = BOAT_DATA.get(id); + if (old.chestBoatEntity != null) { + throw new IllegalStateException("Attempted to replace existing chest boat entity: " + old.chestBoatId()); + } + put(new TerraformBoatData(id, old.boatEntity, chestBoatEntity, old.raftEntity, old.chestRaftEntity)); + } else { + put(new TerraformBoatData(id, null, chestBoatEntity, null, null)); + } + } + + public static void addRaft(Identifier id, EntityType raftEntity) { + if (BOAT_DATA.containsKey(id)) { + TerraformBoatData old = BOAT_DATA.get(id); + if (old.raftEntity != null) { + throw new IllegalStateException("Attempted to replace existing raft entity: " + old.raftId()); + } + put(new TerraformBoatData(id, old.boatEntity, old.chestBoatEntity, raftEntity, old.chestRaftEntity)); + } else { + put(new TerraformBoatData(id, null, null, raftEntity, null)); + } + } + + public static void addChestRaft(Identifier id, EntityType chestRaftEntity) { + if (BOAT_DATA.containsKey(id)) { + TerraformBoatData old = BOAT_DATA.get(id); + if (old.chestRaftEntity != null) { + throw new IllegalStateException("Attempted to replace existing chest raft entity: " + old.chestRaftId()); + } + put(new TerraformBoatData(id, old.boatEntity, old.chestBoatEntity, old.raftEntity, chestRaftEntity)); + } else { + put(new TerraformBoatData(id, null, null, null, chestRaftEntity)); + } + } + + + public static Collection getCollection() { + return BOAT_DATA.values(); + } + + + public Identifier boatId() { + return id.withSuffixedPath("_boat"); + } + + public Identifier chestBoatId() { + return id.withSuffixedPath("_chest_boat"); + } + + public EntityModelLayer boatModelLayer() { + return new EntityModelLayer(id.withPrefixedPath("boat/"), "main"); + } + + public EntityModelLayer chestBoatModelLayer() { + return new EntityModelLayer(id.withPrefixedPath("chest_boat/"), "main"); + } + + public Identifier raftId() { + return id.withSuffixedPath("_raft"); + } + + public Identifier chestRaftId() { + return id.withSuffixedPath("_chest_raft"); + } + + public EntityModelLayer raftModelLayer() { + return new EntityModelLayer(id.withPrefixedPath("raft/"), "main"); + } + + public EntityModelLayer chestRaftModelLayer() { + return new EntityModelLayer(id.withPrefixedPath("chest_raft/"), "main"); + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatDfu.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatDfu.java new file mode 100644 index 00000000..d2b59152 --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatDfu.java @@ -0,0 +1,38 @@ +package com.terraformersmc.terraform.boat.impl.data; + +import com.terraformersmc.terraform.boat.api.data.TerraformBoatDfuApi; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.metadata.ModMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; + +public final class TerraformBoatDfu { + private static final Logger LOGGER = LoggerFactory.getLogger("terraform-boat"); + + private static final Collection REGISTERED_BOATS = new HashSet<>(); + + private TerraformBoatDfu() { + return; + } + + public static void init() { + REGISTERED_BOATS.clear(); + + FabricLoader.getInstance().getEntrypointContainers("terraform-boat-dfu", TerraformBoatDfuApi.class).forEach(entrypoint -> { + ModMetadata metadata = entrypoint.getProvider().getMetadata(); + String modId = metadata.getId(); + try { + REGISTERED_BOATS.addAll(entrypoint.getEntrypoint().getDfuBoatIds()); + } catch (Throwable e) { + LOGGER.error("Mod {} provides a broken implementation of TerraformBoatDfuApi", modId, e); + } + }); + } + + public static Collection getRegisteredBoats() { + return REGISTERED_BOATS; + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitFix.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitFix.java new file mode 100644 index 00000000..28f1f581 --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitFix.java @@ -0,0 +1,95 @@ +package com.terraformersmc.terraform.boat.impl.data; + +import com.mojang.datafixers.*; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.Type; +import com.mojang.serialization.Dynamic; +import net.minecraft.datafixer.FixUtil; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; +import net.minecraft.util.Identifier; + +import java.util.Optional; + +public class TerraformBoatSplitFix extends DataFix { + public TerraformBoatSplitFix(Schema outputSchema, boolean changesType) { + super(outputSchema, changesType); + } + + private static boolean isBoat(String id) { + return id.equals("terraform:boat"); + } + + private static boolean isChestBoat(String id) { + return id.equals("terraform:chest_boat"); + } + + private static boolean isBoatOrChestBoat(String id) { + return isBoat(id) || isChestBoat(id); + } + + private static String getNewBoatIdFromOldType(String type) { + Optional boatData = TerraformBoatData.getOptional(Identifier.of(type)); + String newId = null; + + if (boatData.isPresent()) { + if (boatData.get().boatEntity() != null) { + newId = boatData.get().boatId().toString(); + } else if (boatData.get().raftEntity() != null) { + newId = boatData.get().raftId().toString(); + } + } + + if (newId != null && TerraformBoatDfu.getRegisteredBoats().contains(newId)) { + return newId; + } + + return "minecraft:oak_boat"; + } + + private static String getNewChestBoatIdFromOldType(String type) { + Optional boatData = TerraformBoatData.getOptional(Identifier.of(type)); + String newId = null; + + if (boatData.isPresent()) { + if (boatData.get().chestBoatEntity() != null) { + newId = boatData.get().chestBoatId().toString(); + } else if (boatData.get().chestRaftEntity() != null) { + newId = boatData.get().chestRaftId().toString(); + } + } + + if (newId != null && TerraformBoatDfu.getRegisteredBoats().contains(newId)) { + return newId; + } + + return "minecraft:oak_chest_boat"; + } + + @Override + protected TypeRewriteRule makeRule() { + OpticFinder opticFinder = DSL.fieldFinder("id", IdentifierNormalizingSchema.getIdentifierType()); + Type type = this.getInputSchema().getType(TypeReferences.ENTITY); + Type type2 = this.getOutputSchema().getType(TypeReferences.ENTITY); + + return this.fixTypeEverywhereTyped("TerraformBoatSplitFix", type, type2, typed -> { + Optional optional = typed.getOptional(opticFinder); + + if (optional.isPresent() && isBoatOrChestBoat(optional.get())) { + Dynamic dynamic = typed.getOrCreate(DSL.remainderFinder()); + Optional optional2 = dynamic.get("TerraformBoat").asString().result(); + + String string; + if (isChestBoat(optional.get())) { + string = optional2.map(TerraformBoatSplitFix::getNewChestBoatIdFromOldType).orElse("minecraft:oak_chest_boat"); + } else { + string = optional2.map(TerraformBoatSplitFix::getNewBoatIdFromOldType).orElse("minecraft:oak_boat"); + } + + return FixUtil.withType(type2, typed).update(DSL.remainderFinder(), dynamicx -> dynamicx.remove("TerraformBoat")).set(opticFinder, string); + } else { + return FixUtil.withType(type2, typed); + } + }); + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitSchema.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitSchema.java new file mode 100644 index 00000000..a7b5eeed --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/data/TerraformBoatSplitSchema.java @@ -0,0 +1,36 @@ +package com.terraformersmc.terraform.boat.impl.data; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; + +import java.util.Collection; +import java.util.Map; +import java.util.function.Supplier; + +public class TerraformBoatSplitSchema extends IdentifierNormalizingSchema { + public TerraformBoatSplitSchema(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public Map> registerEntities(Schema schema) { + Map> map = super.registerEntities(schema); + + map.remove("terraform:boat"); + map.remove("terraform:chest_boat"); + + Collection registeredBoats = TerraformBoatDfu.getRegisteredBoats(); + for (String boat : registeredBoats) { + if (boat.contains("_chest") || boat.contains("chest_")) { + schema.register(map, boat, string -> DSL.optionalFields("Items", DSL.list(TypeReferences.ITEM_STACK.in(schema)))); + } else { + schema.registerSimple(map, boat); + } + } + + return map; + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatEntity.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatEntity.java deleted file mode 100644 index 7a4f2ee1..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatEntity.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.entity; - -import java.util.Optional; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.impl.TerraformBoatInitializer; -import com.terraformersmc.terraform.boat.impl.TerraformBoatTrackedData; - -import net.minecraft.entity.EntityType; -import net.minecraft.entity.data.DataTracker; -import net.minecraft.entity.data.TrackedData; -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.item.Item; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; -import net.minecraft.world.World; - -/** - * A {@linkplain BoatEntity boat entity} that stores a {@linkplain TerraformBoatType Terraform boat type}. - */ -public class TerraformBoatEntity extends BoatEntity implements TerraformBoatHolder { - private static final TrackedData> TERRAFORM_BOAT = DataTracker.registerData(TerraformBoatEntity.class, TerraformBoatTrackedData.HANDLER); - - public TerraformBoatEntity(EntityType type, World world) { - super(type, world); - } - - public TerraformBoatEntity(World world) { - this(TerraformBoatInitializer.BOAT, world); - } - - public TerraformBoatEntity(World world, double x, double y, double z) { - this(TerraformBoatInitializer.BOAT, world); - - this.setPosition(x, y, z); - this.prevX = x; - this.prevY = y; - this.prevZ = z; - } - - @Override - public TerraformBoatType getTerraformBoat() { - return this.dataTracker.get(TERRAFORM_BOAT).orElse(null); - } - - @Override - public void setTerraformBoat(TerraformBoatType boat) { - this.dataTracker.set(TERRAFORM_BOAT, Optional.of(boat)); - } - - @Override - protected Text getDefaultName() { - if (this.getTerraformBoat() == null) { - return EntityType.BOAT.getName(); - } - - return super.getDefaultName(); - } - - @Override - public Item asItem() { - return this.getTerraformBoat().getItem(); - } - - @Override - public boolean shouldRender(double cameraX, double cameraY, double cameraZ) { - return this.hasValidTerraformBoat() && super.shouldRender(cameraX, cameraY, cameraZ); - } - - @Override - public void tick() { - if (this.hasValidTerraformBoat()) { - super.tick(); - } else { - this.discard(); - } - } - - @Override - public void setVariant(BoatEntity.Type type) { - return; - } - - @Override - public BoatEntity.Type getVariant() { - return this.getImpersonatedBoatType(); - } - - @Override - protected void initDataTracker(DataTracker.Builder builder) { - super.initDataTracker(builder); - builder.add(TERRAFORM_BOAT, Optional.empty()); - } - - // Serialization - @Override - protected void readCustomDataFromNbt(NbtCompound nbt) { - super.readCustomDataFromNbt(nbt); - this.readTerraformBoatFromNbt(nbt); - - if (!this.hasValidTerraformBoat()) { - this.discard(); - } - } - - @Override - protected void writeCustomDataToNbt(NbtCompound nbt) { - super.writeCustomDataToNbt(nbt); - this.writeTerraformBoatToNbt(nbt); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatHolder.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatHolder.java deleted file mode 100644 index cf74f341..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformBoatHolder.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.entity; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; - -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.Identifier; - -public interface TerraformBoatHolder { - static final String BOAT_KEY = "TerraformBoat"; - - TerraformBoatType getTerraformBoat(); - - void setTerraformBoat(TerraformBoatType boat); - - default boolean hasValidTerraformBoat() { - return this.getTerraformBoat() != null; - } - - default void readTerraformBoatFromNbt(NbtCompound nbt) { - Identifier id = Identifier.tryParse(nbt.getString(BOAT_KEY)); - if (id != null) { - TerraformBoatType boat = TerraformBoatTypeRegistry.INSTANCE.get(id); - if (boat != null) { - this.setTerraformBoat(boat); - } - } - } - - default void writeTerraformBoatToNbt(NbtCompound nbt) { - Identifier boatId = TerraformBoatTypeRegistry.INSTANCE.getId(this.getTerraformBoat()); - if (boatId != null) { - nbt.putString(BOAT_KEY, boatId.toString()); - } - } - - default BoatEntity.Type getImpersonatedBoatType() { - return this.getTerraformBoat().isRaft() ? BoatEntity.Type.BAMBOO : BoatEntity.Type.OAK; - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformChestBoatEntity.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformChestBoatEntity.java deleted file mode 100644 index 39192ab5..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/entity/TerraformChestBoatEntity.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.entity; - -import java.util.Optional; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.impl.TerraformBoatInitializer; -import com.terraformersmc.terraform.boat.impl.TerraformBoatTrackedData; - -import net.minecraft.entity.EntityType; -import net.minecraft.entity.data.DataTracker; -import net.minecraft.entity.data.TrackedData; -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.entity.vehicle.ChestBoatEntity; -import net.minecraft.item.Item; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; -import net.minecraft.world.World; - -/** - * A {@linkplain ChestBoatEntity chest boat entity} that stores a {@linkplain TerraformBoatType Terraform boat type}. - */ -public class TerraformChestBoatEntity extends ChestBoatEntity implements TerraformBoatHolder { - private static final TrackedData> TERRAFORM_BOAT = DataTracker.registerData(TerraformChestBoatEntity.class, TerraformBoatTrackedData.HANDLER); - - public TerraformChestBoatEntity(EntityType type, World world) { - super(type, world); - } - - public TerraformChestBoatEntity(World world) { - this(TerraformBoatInitializer.CHEST_BOAT, world); - } - - public TerraformChestBoatEntity(World world, double x, double y, double z) { - this(TerraformBoatInitializer.CHEST_BOAT, world); - - this.setPosition(x, y, z); - this.prevX = x; - this.prevY = y; - this.prevZ = z; - } - - @Override - public TerraformBoatType getTerraformBoat() { - return this.dataTracker.get(TERRAFORM_BOAT).orElse(null); - } - - @Override - public void setTerraformBoat(TerraformBoatType boat) { - this.dataTracker.set(TERRAFORM_BOAT, Optional.of(boat)); - } - - @Override - protected Text getDefaultName() { - if (this.getTerraformBoat() == null) { - return EntityType.CHEST_BOAT.getName(); - } - - return super.getDefaultName(); - } - - @Override - public Item asItem() { - return this.getTerraformBoat().getChestItem(); - } - - @Override - public boolean shouldRender(double cameraX, double cameraY, double cameraZ) { - return this.hasValidTerraformBoat() && super.shouldRender(cameraX, cameraY, cameraZ); - } - - @Override - public void tick() { - if (this.hasValidTerraformBoat()) { - super.tick(); - } else { - this.discard(); - } - } - - @Override - public void setVariant(BoatEntity.Type type) { - return; - } - - @Override - public BoatEntity.Type getVariant() { - return this.getImpersonatedBoatType(); - } - - @Override - protected void initDataTracker(DataTracker.Builder builder) { - super.initDataTracker(builder); - builder.add(TERRAFORM_BOAT, Optional.empty()); - } - - // Serialization - @Override - protected void readCustomDataFromNbt(NbtCompound nbt) { - super.readCustomDataFromNbt(nbt); - this.readTerraformBoatFromNbt(nbt); - - if (!this.hasValidTerraformBoat()) { - this.discard(); - } - } - - @Override - protected void writeCustomDataToNbt(NbtCompound nbt) { - super.writeCustomDataToNbt(nbt); - this.writeTerraformBoatToNbt(nbt); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatDispenserBehavior.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatDispenserBehavior.java deleted file mode 100644 index fbb3ea40..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatDispenserBehavior.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.item; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatEntity; -import com.terraformersmc.terraform.boat.impl.entity.TerraformChestBoatEntity; - -import net.minecraft.block.DispenserBlock; -import net.minecraft.block.dispenser.DispenserBehavior; -import net.minecraft.block.dispenser.ItemDispenserBehavior; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.tag.FluidTags; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPointer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Vec3d; - -/** - * A {@linkplain DispenserBehavior dispenser behavior} that spawns a {@linkplain TerraformBoatEntity boat entity} with a given {@linkplain TerraformBoatType Terraform boat type}. - */ -public class TerraformBoatDispenserBehavior extends ItemDispenserBehavior { - private static final DispenserBehavior FALLBACK_BEHAVIOR = new ItemDispenserBehavior(); - private static final double OFFSET_MULTIPLIER = 1.125F; - - private final RegistryKey boatKey; - private final boolean chest; - - /** - * @param boatKey a {@linkplain RegistryKey registry key} for the {@linkplain TerraformBoatType Terraform boat type} that should be spawned by this dispenser behavior - * @param chest whether the boat contains a chest - */ - public TerraformBoatDispenserBehavior(RegistryKey boatKey, boolean chest) { - this.boatKey = boatKey; - this.chest = chest; - } - - @Override - public ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { - Direction facing = pointer.state().get(DispenserBlock.FACING); - ServerWorld world = pointer.world(); - Vec3d centerPos = pointer.centerPos(); - - double horizontalOffsetMultiplier = (OFFSET_MULTIPLIER + EntityType.BOAT.getWidth()) / 2.0d; - double x = centerPos.getX() + facing.getOffsetX() * horizontalOffsetMultiplier; - double y = centerPos.getY() + facing.getOffsetY() * OFFSET_MULTIPLIER; - double z = centerPos.getZ() + facing.getOffsetZ() * horizontalOffsetMultiplier; - - BlockPos pos = pointer.pos().offset(facing); - - if (world.getFluidState(pos).isIn(FluidTags.WATER)) { - y += 1.0d; - } else if (!world.getBlockState(pos).isAir() || !world.getFluidState(pos.down()).isIn(FluidTags.WATER)) { - return FALLBACK_BEHAVIOR.dispense(pointer, stack); - } - - TerraformBoatType boatType = TerraformBoatTypeRegistry.INSTANCE.getOrThrow(this.boatKey); - BoatEntity boatEntity; - - if (this.chest) { - TerraformChestBoatEntity chestBoat = new TerraformChestBoatEntity(world, x, y, z); - chestBoat.setTerraformBoat(boatType); - boatEntity = chestBoat; - } else { - TerraformBoatEntity boat = new TerraformBoatEntity(world, x, y, z); - boat.setTerraformBoat(boatType); - boatEntity = boat; - } - - boatEntity.setYaw(facing.asRotation()); - - world.spawnEntity(boatEntity); - - stack.decrement(1); - return stack; - } - - @Override - protected void playSound(BlockPointer pointer) { - pointer.world().syncWorldEvent(1000, pointer.pos(), 0); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItem.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItem.java deleted file mode 100644 index 1dd2f953..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItem.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.item; - -import java.util.List; -import java.util.function.Predicate; - -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatEntity; -import com.terraformersmc.terraform.boat.impl.entity.TerraformChestBoatEntity; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.predicate.entity.EntityPredicates; -import net.minecraft.registry.RegistryKey; -import net.minecraft.stat.Stats; -import net.minecraft.util.Hand; -import net.minecraft.util.TypedActionResult; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.hit.HitResult; -import net.minecraft.util.math.Box; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.RaycastContext; -import net.minecraft.world.World; -import net.minecraft.world.event.GameEvent; - -/** - * An {@linkplain Item item} that spawns a {@linkplain TerraformBoatEntity boat entity} with a given {@linkplain TerraformBoatType Terraform boat type}. - */ -public class TerraformBoatItem extends Item { - private static final Predicate RIDERS = EntityPredicates.EXCEPT_SPECTATOR.and(Entity::canHit); - - private final RegistryKey boatKey; - private final boolean chest; - - /** - * @param boatKey a {@linkplain RegistryKey registry key} for the {@linkplain TerraformBoatType Terraform boat type} that should be spawned by this item - */ - public TerraformBoatItem(RegistryKey boatKey, boolean chest, Item.Settings settings) { - super(settings); - - this.boatKey = boatKey; - this.chest = chest; - } - - public TypedActionResult use(World world, PlayerEntity user, Hand hand) { - ItemStack stack = user.getStackInHand(hand); - - BlockHitResult hitResult = Item.raycast(world, user, RaycastContext.FluidHandling.ANY); - if (hitResult.getType() == HitResult.Type.MISS) { - return TypedActionResult.pass(stack); - } - - Vec3d rotationVec = user.getRotationVec(1f); - List riders = world.getOtherEntities(user, user.getBoundingBox().stretch(rotationVec.multiply(5d)).expand(1d), RIDERS); - - // Prevent collision with user - if (!riders.isEmpty()) { - Vec3d eyePos = user.getEyePos(); - for (Entity entity : riders) { - Box box = entity.getBoundingBox().expand(entity.getTargetingMargin()); - if (box.contains(eyePos)) { - return TypedActionResult.pass(stack); - } - } - } - - // Spawn boat entity - if (hitResult.getType() == HitResult.Type.BLOCK) { - double x = hitResult.getPos().x; - double y = hitResult.getPos().y; - double z = hitResult.getPos().z; - - TerraformBoatType boatType = TerraformBoatTypeRegistry.INSTANCE.getOrThrow(this.boatKey); - BoatEntity boatEntity; - - if (this.chest) { - TerraformChestBoatEntity chestBoat = new TerraformChestBoatEntity(world, x, y, z); - chestBoat.setTerraformBoat(boatType); - boatEntity = chestBoat; - } else { - TerraformBoatEntity boat = new TerraformBoatEntity(world, x, y, z); - boat.setTerraformBoat(boatType); - boatEntity = boat; - } - - boatEntity.setYaw(user.getYaw()); - - if (!world.isSpaceEmpty(boatEntity, boatEntity.getBoundingBox().expand(-0.1d))) { - return TypedActionResult.fail(stack); - } - - if (!world.isClient()) { - world.spawnEntity(boatEntity); - world.emitGameEvent(user, GameEvent.ENTITY_PLACE, hitResult.getPos()); - - if (!user.getAbilities().creativeMode) { - stack.decrement(1); - } - } - - user.incrementStat(Stats.USED.getOrCreateStat(this)); - return TypedActionResult.success(stack, world.isClient()); - } - - return TypedActionResult.pass(stack); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItemHelperImpl.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItemHelperImpl.java new file mode 100644 index 00000000..61a48e2b --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/item/TerraformBoatItemHelperImpl.java @@ -0,0 +1,113 @@ +package com.terraformersmc.terraform.boat.impl.item; + +import com.terraformersmc.terraform.boat.impl.data.TerraformBoatData; +import net.minecraft.block.DispenserBlock; +import net.minecraft.block.dispenser.BoatDispenserBehavior; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.SpawnGroup; +import net.minecraft.entity.vehicle.*; +import net.minecraft.item.BoatItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemConvertible; +import net.minecraft.item.Items; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.util.Identifier; + +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +public final class TerraformBoatItemHelperImpl { + private TerraformBoatItemHelperImpl() { + return; + } + + private static EntityType.EntityFactory getBoatFactory(Supplier itemSupplier) { + return (type, world) -> new BoatEntity(type, world, itemSupplier); + } + + private static EntityType.EntityFactory getChestBoatFactory(Supplier itemSupplier) { + return (type, world) -> new ChestBoatEntity(type, world, itemSupplier); + } + + private static EntityType.EntityFactory getRaftFactory(Supplier itemSupplier) { + return (type, world) -> new RaftEntity(type, world, itemSupplier); + } + + private static EntityType.EntityFactory getChestRaftFactory(Supplier itemSupplier) { + return (type, world) -> new ChestRaftEntity(type, world, itemSupplier); + } + + private static EntityType.Builder createEntityTypeBuilder(EntityType.EntityFactory factory) { + return EntityType.Builder.create(factory, SpawnGroup.MISC) + .dropsNothing() + .dimensions(1.375f, 0.5625f) + .eyeHeight(0.5625f) + .maxTrackingRange(10); + } + + private static EntityType registerEntityType(Identifier id, EntityType.Builder type) { + RegistryKey> key = RegistryKey.of(RegistryKeys.ENTITY_TYPE, id); + + return Registry.register(Registries.ENTITY_TYPE, key, type.build(key)); + } + + private static BoatItem registerBoat(Identifier id, Identifier boatId, Item.Settings settings, Function, EntityType.EntityFactory> factory, BiConsumer> registry) { + DelayedItemSupplier itemSupplier = new DelayedItemSupplier(); + EntityType entityType = registerEntityType(boatId, createEntityTypeBuilder(factory.apply(itemSupplier))); + + RegistryKey itemKey = RegistryKey.of(RegistryKeys.ITEM, boatId); + BoatItem item = Registry.register(Registries.ITEM, itemKey, new BoatItem(entityType, settings.registryKey(itemKey))); + + itemSupplier.set(item); + registry.accept(id, entityType); + registerBoatDispenserBehavior(item, entityType); + + return item; + } + + + public static BoatItem registerBoatItem(Identifier id, Item.Settings settings, boolean chest, boolean raft) { + TerraformBoatData boatData = TerraformBoatData.empty(id); + + if (raft) { + if (chest) { + return registerBoat(id, boatData.chestRaftId(), settings, + TerraformBoatItemHelperImpl::getChestRaftFactory, TerraformBoatData::addChestRaft); + } else { + return registerBoat(id, boatData.raftId(), settings, + TerraformBoatItemHelperImpl::getRaftFactory, TerraformBoatData::addRaft); + } + } else { + if (chest) { + return registerBoat(id, boatData.chestBoatId(), settings, + TerraformBoatItemHelperImpl::getChestBoatFactory, TerraformBoatData::addChestBoat); + } else { + return registerBoat(id, boatData.boatId(), settings, + TerraformBoatItemHelperImpl::getBoatFactory, TerraformBoatData::addBoat); + } + } + } + + public static void registerBoatDispenserBehavior(ItemConvertible item, EntityType boatEntity) { + DispenserBlock.registerBehavior(item, new BoatDispenserBehavior(boatEntity)); + } + + + private static class DelayedItemSupplier implements Supplier { + Item value = Items.AIR; + + public void set(Item value) { + this.value = value; + } + + @Override + public Item get() { + return this.value; + } + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntity.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntity.java deleted file mode 100644 index 0e5b2107..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntity.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArg; - -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatHolder; - -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.item.ItemConvertible; - -@Mixin(BoatEntity.class) -public class MixinBoatEntity { - @ModifyArg(method = "fall(DZLnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;)V", index = 0, at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/vehicle/BoatEntity;dropItem(Lnet/minecraft/item/ItemConvertible;)Lnet/minecraft/entity/ItemEntity;", ordinal = 0)) - private ItemConvertible terraformWood$replacePlanksDropItem(ItemConvertible original) { - if (this instanceof TerraformBoatHolder terraformBoatHolder) { - return terraformBoatHolder.getTerraformBoat().getPlanks(); - } - - return original; - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntityRenderer.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntityRenderer.java deleted file mode 100644 index 1c3b584f..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinBoatEntityRenderer.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.mixin; - -import java.util.Map; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.mojang.datafixers.util.Pair; -import com.terraformersmc.terraform.boat.impl.client.TerraformBoatEntityRenderer; -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatHolder; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.render.entity.BoatEntityRenderer; -import net.minecraft.client.render.entity.model.BoatEntityModel; -import net.minecraft.entity.vehicle.BoatEntity; -import net.minecraft.util.Identifier; - -@Mixin(BoatEntityRenderer.class) -@Environment(EnvType.CLIENT) -public class MixinBoatEntityRenderer { - @WrapOperation(method = "render", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;")) - @SuppressWarnings("unused") - private Object terraformWood$getBoatTextureAndModel(Map> instance, Object type, Operation original, BoatEntity entity) { - //noinspection ConstantConditions - if (entity instanceof TerraformBoatHolder terraformEntity && - (Object) this instanceof TerraformBoatEntityRenderer terraformRenderer) { - return terraformRenderer.getTextureAndModel(terraformEntity); - } - - return original.call(instance, type); - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinEntityPredicate.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinEntityPredicate.java deleted file mode 100644 index fb3b77eb..00000000 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinEntityPredicate.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.terraformersmc.terraform.boat.impl.mixin; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.terraformersmc.terraform.boat.impl.TerraformBoatInitializer; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; -import net.minecraft.predicate.entity.EntityPredicate; - -@Mixin(EntityPredicate.class) -public class MixinEntityPredicate { - @WrapOperation( - method = "test(Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/entity/Entity;)Z", - at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getType()Lnet/minecraft/entity/EntityType;") - ) - private EntityType terraformWood$useVanillaBoatTypeForPredicates(Entity entity, Operation> operation) { - EntityType type = operation.call(entity); - - if (type == TerraformBoatInitializer.BOAT) { - return EntityType.BOAT; - } else if (type == TerraformBoatInitializer.CHEST_BOAT) { - return EntityType.CHEST_BOAT; - } - - return type; - } -} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinSchemas.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinSchemas.java new file mode 100644 index 00000000..e2c2a817 --- /dev/null +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/boat/impl/mixin/MixinSchemas.java @@ -0,0 +1,69 @@ +package com.terraformersmc.terraform.boat.impl.mixin; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.schemas.Schema; +import com.terraformersmc.terraform.boat.impl.data.TerraformAddBoatsSchema; +import com.terraformersmc.terraform.boat.impl.data.TerraformBoatDfu; +import com.terraformersmc.terraform.boat.impl.data.TerraformBoatSplitFix; +import com.terraformersmc.terraform.boat.impl.data.TerraformBoatSplitSchema; +import net.minecraft.datafixer.Schemas; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.fix.ChoiceTypesFix; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.function.BiFunction; + +@Mixin(Schemas.class) +public class MixinSchemas { + /* + * The following provides a mechanism for mods to register their boat IDs early enough for the DFU. + * This is required in order for Terraform boats to be upgraded from <1.21.2 to after >=1.21.2. + * Boats not registered in this manner will be converted to oak wood type at upgrade. + */ + @Inject(method = "create", at = @At("HEAD")) + private static void create(CallbackInfoReturnable cir) { + TerraformBoatDfu.init(); + } + + /* + * This fix adds the legacy Terraform API place-holder boat entities to the DFU as of just before 1.20. + * This means worlds with Terraform-based boats must be upgraded to 1.20 or 1.21 before 1.21.2+. + */ + @WrapOperation(method = "build", + slice = @Slice( + from = @At(value = "NEW", target = "net/minecraft/datafixer/fix/ChunkDeleteLightFix") + ), + at = @At(value = "INVOKE", target = "Lcom/mojang/datafixers/DataFixerBuilder;addSchema(ILjava/util/function/BiFunction;)Lcom/mojang/datafixers/schemas/Schema;", ordinal = 0) + ) + @SuppressWarnings("unused") + private static Schema terraform$injectAddBoatsFix(DataFixerBuilder builder, int version, BiFunction factory, Operation original) { + Schema addBoatsSchema = builder.addSchema(3454, TerraformAddBoatsSchema::new); + builder.addFixer(new ChoiceTypesFix(addBoatsSchema, "Add Terraform Boats", TypeReferences.ENTITY)); + + return original.call(builder, version, factory); + } + + /* + * This fix updates Terraform API place-holder boat entities, converting them to the new split entities, + * and simultaneously removing the old Terraform API system of using fake boat entities. + */ + @WrapOperation(method = "build", + slice = @Slice( + from = @At(value = "NEW", target = "net/minecraft/datafixer/fix/FireResistantToDamageResistantComponentFix") + ), + at = @At(value = "INVOKE", target = "Lcom/mojang/datafixers/DataFixerBuilder;addSchema(ILjava/util/function/BiFunction;)Lcom/mojang/datafixers/schemas/Schema;", ordinal = 0) + ) + @SuppressWarnings("unused") + private static Schema terraform$injectBoatSplitFix(DataFixerBuilder builder, int version, BiFunction factory, Operation original) { + Schema boatSplitSchema = builder.addSchema(4066, TerraformBoatSplitSchema::new); + builder.addFixer(new TerraformBoatSplitFix(boatSplitSchema, true)); + + return original.call(builder, version, factory); + } +} diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/ExtendedLeavesBlock.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/ExtendedLeavesBlock.java index 714e4027..80fce3e5 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/ExtendedLeavesBlock.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/ExtendedLeavesBlock.java @@ -12,8 +12,9 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.random.Random; -import net.minecraft.world.BlockView; import net.minecraft.world.WorldAccess; +import net.minecraft.world.WorldView; +import net.minecraft.world.tick.ScheduledTickView; /** * A leaves block with extended range, permitting leaves to be as far as 13 blocks away from the tree rather than the @@ -50,19 +51,19 @@ public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Ran } @Override - public int getOpacity(BlockState state, BlockView view, BlockPos pos) { + public int getOpacity(BlockState state) { return 0; } @Override - public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { + public BlockState getStateForNeighborUpdate(BlockState state, WorldView world, ScheduledTickView tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, Random random) { if (state.get(WATERLOGGED)) { - world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world)); + tickView.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world)); } int distance = ExtendedLeavesBlock.getDistanceFromLog(neighborState) + 1; if (distance != 1 || state.get(DISTANCE) != distance) { - world.scheduleBlockTick(pos, this, 1); + tickView.scheduleBlockTick(pos, this, 1); } return state; diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/LeafPileBlock.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/LeafPileBlock.java index 17acd5e5..4488fb2a 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/LeafPileBlock.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/LeafPileBlock.java @@ -7,10 +7,11 @@ import net.minecraft.registry.tag.FluidTags; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.BlockView; -import net.minecraft.world.WorldAccess; import net.minecraft.world.WorldView; +import net.minecraft.world.tick.ScheduledTickView; /** * A very thin block that is intended to be used like a carpet block, but for leaves. @@ -28,8 +29,8 @@ public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos } @Override - public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighbor) { - return !state.canPlaceAt(world, pos) ? Blocks.AIR.getDefaultState() : super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighbor); + public BlockState getStateForNeighborUpdate(BlockState state, WorldView world, ScheduledTickView tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, Random random) { + return !state.canPlaceAt(world, pos) ? Blocks.AIR.getDefaultState() : super.getStateForNeighborUpdate(state, world, tickView, pos, direction, neighborPos, neighborState, random); } @Override diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/TransparentLeavesBlock.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/TransparentLeavesBlock.java index 517e8f6d..466b6ba1 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/TransparentLeavesBlock.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/leaves/api/block/TransparentLeavesBlock.java @@ -3,8 +3,6 @@ import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.LeavesBlock; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockView; /** * A leaf block that does not block light. @@ -15,7 +13,7 @@ public TransparentLeavesBlock(Block.Settings settings) { } @Override - public int getOpacity(BlockState state, BlockView view, BlockPos pos) { + public int getOpacity(BlockState state) { return 0; } } diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/sign/impl/mixin/MixinSignEditScreen.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/sign/impl/mixin/MixinSignEditScreen.java index 14879e0e..7e8a7aa9 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/sign/impl/mixin/MixinSignEditScreen.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/sign/impl/mixin/MixinSignEditScreen.java @@ -7,7 +7,9 @@ import net.fabricmc.api.Environment; import net.minecraft.block.BlockState; import net.minecraft.block.WoodType; +import net.minecraft.block.entity.SignBlockEntity; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen; import net.minecraft.client.gui.screen.ingame.SignEditScreen; import net.minecraft.client.render.TexturedRenderLayers; import net.minecraft.client.util.SpriteIdentifier; @@ -16,13 +18,20 @@ @Mixin(SignEditScreen.class) @Environment(EnvType.CLIENT) -public class MixinSignEditScreen { +public abstract class MixinSignEditScreen extends AbstractSignEditScreen { + public MixinSignEditScreen(SignBlockEntity blockEntity, boolean front, boolean filtered) { + super(blockEntity, front, filtered); + } + @WrapOperation( - method = "renderSignBackground", + // DrawContext#draw callback within the renderSignBackground method + method = "method_64048", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/TexturedRenderLayers;getSignTextureId(Lnet/minecraft/block/WoodType;)Lnet/minecraft/client/util/SpriteIdentifier;") ) @SuppressWarnings("unused") - private SpriteIdentifier terraformWood$editSignTextureId(WoodType type, Operation original, DrawContext drawContext, BlockState state) { + private SpriteIdentifier terraformWood$editSignTextureId(WoodType type, Operation original, DrawContext drawContext) { + BlockState state = this.blockEntity.getCachedState(); + if (state.getBlock() instanceof TerraformSign signBlock) { return new SpriteIdentifier(TexturedRenderLayers.SIGNS_ATLAS_TEXTURE, signBlock.getTexture()); } diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/BareSmallLogBlock.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/BareSmallLogBlock.java index 12fa9d2d..65d27243 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/BareSmallLogBlock.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/BareSmallLogBlock.java @@ -318,7 +318,7 @@ public BlockState getNeighborUpdateState(BlockState state, Direction fromDirecti } @Override - public boolean isTransparent(BlockState state, BlockView world, BlockPos pos) { + public boolean isTransparent(BlockState state) { return !state.get(WATERLOGGED); } diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/PillarLogHelper.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/PillarLogHelper.java index 3d84acf7..2e6dec88 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/PillarLogHelper.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/PillarLogHelper.java @@ -12,70 +12,66 @@ private PillarLogHelper() { } /** - * Factory to create a PillarBlock log with default settings and + * Factory to create default block settings for a PillarBlock log with * the same map color on all block faces. * * @param color Map color for all faces of log - * @return New PillarBlock + * @return New AbstractBlock.Settings */ - public static PillarBlock of(MapColor color) { - return new PillarBlock(AbstractBlock.Settings.create() + public static AbstractBlock.Settings createSettings(MapColor color) { + return AbstractBlock.Settings.create() .mapColor(color) .instrument(NoteBlockInstrument.BASS) .strength(2.0F) .sounds(BlockSoundGroup.WOOD) - .burnable() - ); + .burnable(); } /** - * Factory to create a PillarBlock log with default settings and + * Factory to create default block settings for a PillarBlock log with * different map colors on the top/bottom versus the sides. * * @param wood Map color for non-bark faces of log (ends) * @param bark Map color for bark faces of log (sides) - * @return New PillarBlock + * @return New AbstractBlock.Settings */ - public static PillarBlock of(MapColor wood, MapColor bark) { - return new PillarBlock(AbstractBlock.Settings.create() + public static AbstractBlock.Settings createSettings(MapColor wood, MapColor bark) { + return AbstractBlock.Settings.create() .mapColor((state) -> Direction.Axis.Y.equals(state.get(PillarBlock.AXIS)) ? wood : bark) .instrument(NoteBlockInstrument.BASS) .strength(2.0F) .sounds(BlockSoundGroup.WOOD) - .burnable() - ); + .burnable(); } /** - * Factory to create a PillarBlock Nether stem with default settings and + * Factory to create default block settings for a PillarBlock Nether stem with * the same map color on all block faces. * * @param color Map color for all faces of stem - * @return New PillarBlock + * @return New AbstractBlock.Settings */ - public static PillarBlock ofNether(MapColor color) { - return new PillarBlock(AbstractBlock.Settings.create() + public static AbstractBlock.Settings createNetherSettings(MapColor color) { + return AbstractBlock.Settings.create() .mapColor(color) .instrument(NoteBlockInstrument.BASS) .strength(2.0F) - .sounds(BlockSoundGroup.NETHER_STEM) - ); + .sounds(BlockSoundGroup.NETHER_STEM); } /** - * Factory to create a PillarBlock Nether stem with default settings and + * Factory to create default block settings for a PillarBlock Nether stem with * different map colors on the top/bottom versus the sides. * * @param wood Map color for non-bark faces of stem (ends) * @param bark Map color for bark faces of stem (sides) - * @return New PillarBlock + * @return New AbstractBlock.Settings */ - public static PillarBlock ofNether(MapColor wood, MapColor bark) { - return new PillarBlock(AbstractBlock.Settings.create() + public static AbstractBlock.Settings createNetherSettings(MapColor wood, MapColor bark) { + return AbstractBlock.Settings.create() .mapColor((state) -> Direction.Axis.Y.equals(state.get(PillarBlock.AXIS)) ? wood : bark) .instrument(NoteBlockInstrument.BASS) .strength(2.0F) - .sounds(BlockSoundGroup.NETHER_STEM) - ); + .sounds(BlockSoundGroup.NETHER_STEM); } } diff --git a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/SmallLogBlock.java b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/SmallLogBlock.java index 007ff130..143d2acb 100644 --- a/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/SmallLogBlock.java +++ b/terraform-wood-api-v1/src/main/java/com/terraformersmc/terraform/wood/api/block/SmallLogBlock.java @@ -206,7 +206,7 @@ public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos p } @Override - public VoxelShape getCullingShape(BlockState state, BlockView world, BlockPos pos) { + public VoxelShape getCullingShape(BlockState state) { return this.collisionShapes[this.getShapeIndex(state)]; } diff --git a/terraform-wood-api-v1/src/main/resources/fabric.mod.json b/terraform-wood-api-v1/src/main/resources/fabric.mod.json index 6dc2c4b7..3618c281 100644 --- a/terraform-wood-api-v1/src/main/resources/fabric.mod.json +++ b/terraform-wood-api-v1/src/main/resources/fabric.mod.json @@ -6,10 +6,8 @@ "environment": "*", "entrypoints": { "main": [ - "com.terraformersmc.terraform.boat.impl.TerraformBoatInitializer" ], "client": [ - "com.terraformersmc.terraform.boat.impl.client.TerraformBoatClientInitializer" ] }, "license": "LGPL-3.0-only", @@ -60,9 +58,9 @@ } }, "mixins": [ - "mixins.terraform-boats.json", + "mixins.terraform-boat.json", "mixins.terraform-leaves.json", - "mixins.terraform-signs.json", + "mixins.terraform-sign.json", "mixins.terraform-wood.json" ] } diff --git a/terraform-wood-api-v1/src/main/resources/mixins.terraform-boats.json b/terraform-wood-api-v1/src/main/resources/mixins.terraform-boat.json similarity index 65% rename from terraform-wood-api-v1/src/main/resources/mixins.terraform-boats.json rename to terraform-wood-api-v1/src/main/resources/mixins.terraform-boat.json index ddee5287..6846b0bc 100644 --- a/terraform-wood-api-v1/src/main/resources/mixins.terraform-boats.json +++ b/terraform-wood-api-v1/src/main/resources/mixins.terraform-boat.json @@ -3,11 +3,7 @@ "package": "com.terraformersmc.terraform.boat.impl.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ - "MixinBoatEntity", - "MixinEntityPredicate" - ], - "client": [ - "MixinBoatEntityRenderer" + "MixinSchemas" ], "injectors": { "defaultRequire": 1 diff --git a/terraform-wood-api-v1/src/main/resources/mixins.terraform-signs.json b/terraform-wood-api-v1/src/main/resources/mixins.terraform-sign.json similarity index 100% rename from terraform-wood-api-v1/src/main/resources/mixins.terraform-signs.json rename to terraform-wood-api-v1/src/main/resources/mixins.terraform-sign.json diff --git a/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTest.java b/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTest.java index 4a792c54..33e75d78 100644 --- a/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTest.java +++ b/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTest.java @@ -1,43 +1,32 @@ package com.terraformersmc.terraform.wood.test; -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; import com.terraformersmc.terraform.boat.api.item.TerraformBoatItemHelper; import com.terraformersmc.terraform.sign.api.block.TerraformHangingSignBlock; import com.terraformersmc.terraform.sign.api.block.TerraformSignBlock; import com.terraformersmc.terraform.sign.api.block.TerraformWallHangingSignBlock; import com.terraformersmc.terraform.sign.api.block.TerraformWallSignBlock; -import com.terraformersmc.terraform.wood.test.command.SpawnBoatsCommand; +import com.terraformersmc.terraform.wood.api.block.PillarLogHelper; +import com.terraformersmc.terraform.wood.test.command.SpawnBoatsCommand; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; -import net.minecraft.item.HangingSignItem; -import net.minecraft.item.Item; -import net.minecraft.item.ItemGroups; -import net.minecraft.item.Items; -import net.minecraft.item.SignItem; +import net.minecraft.block.*; +import net.minecraft.item.*; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.sound.BlockSoundGroup; import net.minecraft.util.Identifier; public class TerraformWoodTest implements ModInitializer { private static final String MOD_ID = "terraform"; + private static final Identifier CUSTOM_LOG_ID = Identifier.of(MOD_ID, "custom_log"); private static final Identifier CUSTOM_PLANKS_ID = Identifier.of(MOD_ID, "custom_planks"); - protected static final Identifier CUSTOM_BOAT_ID = Identifier.of(MOD_ID, "custom_boat"); - private static final Identifier CUSTOM_CHEST_BOAT_ID = Identifier.of(MOD_ID, "custom_chest_boat"); - protected static final Identifier CUSTOM_RAFT_ID = Identifier.of(MOD_ID, "custom_raft"); - private static final Identifier CUSTOM_CHEST_RAFT_ID = Identifier.of(MOD_ID, "custom_chest_raft"); - - public static final RegistryKey CUSTOM_BOAT_KEY = TerraformBoatTypeRegistry.createKey(CUSTOM_BOAT_ID); - public static final RegistryKey CUSTOM_RAFT_KEY = TerraformBoatTypeRegistry.createKey(CUSTOM_RAFT_ID); + public static final Identifier CUSTOM_BOATS_ID = Identifier.of(MOD_ID, "custom"); protected static final Identifier SIGN_TEXTURE_ID = Identifier.of(MOD_ID, "entity/signs/custom"); protected static final Identifier HANGING_SIGN_TEXTURE_ID = Identifier.of(MOD_ID, "entity/signs/hanging/custom"); @@ -47,63 +36,71 @@ public class TerraformWoodTest implements ModInitializer { private static final Identifier CUSTOM_HANGING_SIGN_ID = Identifier.of(MOD_ID, "custom_hanging_sign"); private static final Identifier CUSTOM_WALL_HANGING_SIGN_ID = Identifier.of(MOD_ID, "custom_wall_hanging_sign"); + public static BoatItem customBoatItem; + public static BoatItem customChestBoatItem; + public static BoatItem customRaftItem; + public static BoatItem customChestRaftItem; + @Override public void onInitialize() { - Item planks = new Item(new Item.Settings()); + Block customLog = new PillarBlock(PillarLogHelper.createSettings(MapColor.RED, MapColor.BLUE).registryKey(RegistryKey.of(RegistryKeys.BLOCK, CUSTOM_LOG_ID))); + Block customPlanks = new Block(AbstractBlock.Settings.create().mapColor(MapColor.RED).registryKey(RegistryKey.of(RegistryKeys.BLOCK, CUSTOM_PLANKS_ID))); - // Boats - Item boatItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_BOAT_ID, CUSTOM_BOAT_KEY, false); - Item chestBoatItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_CHEST_BOAT_ID, CUSTOM_BOAT_KEY, true); - - Item raftItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_RAFT_ID, CUSTOM_RAFT_KEY, false); - Item chestRaftItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_CHEST_RAFT_ID, CUSTOM_RAFT_KEY, true); + BlockItem customLogItem = new BlockItem(customLog, new Item.Settings().registryKey(RegistryKey.of(RegistryKeys.ITEM, CUSTOM_LOG_ID))); + BlockItem customPlanksItem = new BlockItem(customPlanks, new Item.Settings().registryKey(RegistryKey.of(RegistryKeys.ITEM, CUSTOM_PLANKS_ID))); - TerraformBoatType boat = new TerraformBoatType.Builder() - .item(boatItem) - .chestItem(chestBoatItem) - .planks(planks) - .build(); + // Boats + customBoatItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_BOATS_ID, false); + customChestBoatItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_BOATS_ID, true); - TerraformBoatType raft = new TerraformBoatType.Builder() - .raft() - .item(raftItem) - .chestItem(chestRaftItem) - .planks(planks) - .build(); + customRaftItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_BOATS_ID, false, true); + customChestRaftItem = TerraformBoatItemHelper.registerBoatItem(CUSTOM_BOATS_ID, true, true); // Signs - Block sign = new TerraformSignBlock(SIGN_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_SIGN).sounds(BlockSoundGroup.ANVIL)); + Block sign = new TerraformSignBlock(SIGN_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_SIGN).sounds(BlockSoundGroup.ANVIL).registryKey(RegistryKey.of(RegistryKeys.BLOCK, CUSTOM_SIGN_ID))); Registry.register(Registries.BLOCK, CUSTOM_SIGN_ID, sign); - Block wallSign = new TerraformWallSignBlock(SIGN_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_WALL_SIGN).sounds(BlockSoundGroup.SAND).dropsLike(sign)); + Block wallSign = new TerraformWallSignBlock(SIGN_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_WALL_SIGN).sounds(BlockSoundGroup.SAND).lootTable(sign.getLootTableKey()).registryKey(RegistryKey.of(RegistryKeys.BLOCK, CUSTOM_WALL_SIGN_ID))); Registry.register(Registries.BLOCK, CUSTOM_WALL_SIGN_ID, wallSign); - Block hangingSign = new TerraformHangingSignBlock(HANGING_SIGN_TEXTURE_ID, HANGING_SIGN_GUI_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_HANGING_SIGN).sounds(BlockSoundGroup.WOOL)); + Block hangingSign = new TerraformHangingSignBlock(HANGING_SIGN_TEXTURE_ID, HANGING_SIGN_GUI_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_HANGING_SIGN).sounds(BlockSoundGroup.WOOL).registryKey(RegistryKey.of(RegistryKeys.BLOCK, CUSTOM_HANGING_SIGN_ID))); Registry.register(Registries.BLOCK, CUSTOM_HANGING_SIGN_ID, hangingSign); - Block wallHangingSign = new TerraformWallHangingSignBlock(HANGING_SIGN_TEXTURE_ID, HANGING_SIGN_GUI_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_WALL_HANGING_SIGN).sounds(BlockSoundGroup.SCULK_SENSOR).dropsLike(hangingSign)); + Block wallHangingSign = new TerraformWallHangingSignBlock(HANGING_SIGN_TEXTURE_ID, HANGING_SIGN_GUI_TEXTURE_ID, AbstractBlock.Settings.copy(Blocks.OAK_WALL_HANGING_SIGN).sounds(BlockSoundGroup.SCULK_SENSOR).lootTable(hangingSign.getLootTableKey()).registryKey(RegistryKey.of(RegistryKeys.BLOCK, CUSTOM_WALL_HANGING_SIGN_ID))); Registry.register(Registries.BLOCK, CUSTOM_WALL_HANGING_SIGN_ID, wallHangingSign); - Item signItem = new SignItem(new Item.Settings().maxCount(16), sign, wallSign); - Item hangingSignItem = new HangingSignItem(hangingSign, wallHangingSign, new Item.Settings().maxCount(16)); + SignItem signItem = new SignItem(sign, wallSign, new Item.Settings().maxCount(16).registryKey(RegistryKey.of(RegistryKeys.ITEM, CUSTOM_SIGN_ID))); + HangingSignItem hangingSignItem = new HangingSignItem(hangingSign, wallHangingSign, new Item.Settings().maxCount(16).registryKey(RegistryKey.of(RegistryKeys.ITEM, CUSTOM_HANGING_SIGN_ID))); // Register - Registry.register(TerraformBoatTypeRegistry.INSTANCE, CUSTOM_BOAT_KEY, boat); - Registry.register(TerraformBoatTypeRegistry.INSTANCE, CUSTOM_RAFT_KEY, raft); + customLogItem.appendBlocks(Item.BLOCK_ITEMS, customLogItem); + customPlanksItem.appendBlocks(Item.BLOCK_ITEMS, customPlanksItem); + signItem.appendBlocks(Item.BLOCK_ITEMS, signItem); + hangingSignItem.appendBlocks(Item.BLOCK_ITEMS, hangingSignItem); - Registry.register(Registries.ITEM, CUSTOM_PLANKS_ID, planks); + Registry.register(Registries.BLOCK, CUSTOM_LOG_ID, customLog); + Registry.register(Registries.BLOCK, CUSTOM_PLANKS_ID, customPlanks); + + Registry.register(Registries.ITEM, CUSTOM_LOG_ID, customLogItem); + Registry.register(Registries.ITEM, CUSTOM_PLANKS_ID, customPlanksItem); Registry.register(Registries.ITEM, CUSTOM_SIGN_ID, signItem); Registry.register(Registries.ITEM, CUSTOM_HANGING_SIGN_ID, hangingSignItem); ItemGroupEvents.modifyEntriesEvent(ItemGroups.BUILDING_BLOCKS).register(entries -> { - entries.addAfter(Items.MANGROVE_PLANKS, planks); - entries.addAfter(Items.MANGROVE_CHEST_BOAT, boatItem, chestBoatItem, raftItem, chestRaftItem); - entries.addAfter(Items.MANGROVE_HANGING_SIGN, signItem, hangingSignItem); + entries.addAfter(Items.CHERRY_BUTTON, customLogItem, customPlanksItem); }); - // Utility commands - CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - SpawnBoatsCommand.register(dispatcher); + ItemGroupEvents.modifyEntriesEvent(ItemGroups.FUNCTIONAL).register(entries -> { + entries.addAfter(Items.CHERRY_HANGING_SIGN, signItem, hangingSignItem); + }); + + ItemGroupEvents.modifyEntriesEvent(ItemGroups.TOOLS).register(entries -> { + entries.addAfter(Items.CHERRY_CHEST_BOAT, customBoatItem, customChestBoatItem, customRaftItem, customChestRaftItem); }); + + // Utility commands + CommandRegistrationCallback.EVENT.register( + (dispatcher, registryAccess, environment) -> SpawnBoatsCommand.register(dispatcher) + ); } } diff --git a/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTestClient.java b/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTestClient.java index f72697b8..c8c39b77 100644 --- a/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTestClient.java +++ b/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/TerraformWoodTestClient.java @@ -7,7 +7,6 @@ public class TerraformWoodTestClient implements ClientModInitializer { @Override public void onInitializeClient() { - TerraformBoatClientHelper.registerModelLayers(TerraformWoodTest.CUSTOM_BOAT_ID, false); - TerraformBoatClientHelper.registerModelLayers(TerraformWoodTest.CUSTOM_RAFT_ID, true); + TerraformBoatClientHelper.registerModelLayers(TerraformWoodTest.CUSTOM_BOATS_ID); } } diff --git a/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/command/SpawnBoatsCommand.java b/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/command/SpawnBoatsCommand.java index 1dd7ece7..d823d29a 100644 --- a/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/command/SpawnBoatsCommand.java +++ b/terraform-wood-api-v1/src/testmod/java/com/terraformersmc/terraform/wood/test/command/SpawnBoatsCommand.java @@ -3,9 +3,7 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; -import com.terraformersmc.terraform.boat.api.TerraformBoatType; -import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; -import com.terraformersmc.terraform.boat.impl.entity.TerraformBoatEntity; +import com.terraformersmc.terraform.boat.impl.data.TerraformBoatData; import com.terraformersmc.terraform.wood.test.TerraformWoodTest; import net.minecraft.advancement.AdvancementEntry; @@ -15,6 +13,10 @@ import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.mob.ShulkerEntity; import net.minecraft.entity.passive.GoatEntity; +import net.minecraft.entity.vehicle.BoatEntity; +import net.minecraft.entity.vehicle.ChestBoatEntity; +import net.minecraft.entity.vehicle.ChestRaftEntity; +import net.minecraft.entity.vehicle.RaftEntity; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; @@ -39,8 +41,7 @@ private static int execute(CommandContext context) { ServerWorld world = source.getWorld(); Vec3d pos = source.getPosition(); - TerraformBoatType boatType = TerraformBoatTypeRegistry.INSTANCE.getOrThrow(TerraformWoodTest.CUSTOM_BOAT_KEY); - TerraformBoatType raftType = TerraformBoatTypeRegistry.INSTANCE.getOrThrow(TerraformWoodTest.CUSTOM_RAFT_KEY); + TerraformBoatData boatData = TerraformBoatData.get(TerraformWoodTest.CUSTOM_BOATS_ID); // Revoke advancement ServerPlayerEntity player = source.getPlayer(); @@ -60,20 +61,20 @@ private static int execute(CommandContext context) { } // Spawn boats - TerraformBoatEntity boat = new TerraformBoatEntity(world, pos.getX(), pos.getY(), pos.getZ()); - boat.setTerraformBoat(boatType); + BoatEntity boat = new BoatEntity(boatData.boatEntity(), world, () -> TerraformWoodTest.customBoatItem); + boat.setPos(pos.getX(), pos.getY(), pos.getZ()); world.spawnEntity(boat); - TerraformBoatEntity chestBoat = new TerraformBoatEntity(world, pos.getX() - 2, pos.getY(), pos.getZ()); - chestBoat.setTerraformBoat(boatType); + ChestBoatEntity chestBoat = new ChestBoatEntity(boatData.chestBoatEntity(), world, () -> TerraformWoodTest.customChestBoatItem); + chestBoat.setPos(pos.getX() - 2, pos.getY(), pos.getZ()); world.spawnEntity(chestBoat); - TerraformBoatEntity raft = new TerraformBoatEntity(world, pos.getX() - 4, pos.getY(), pos.getZ()); - raft.setTerraformBoat(raftType); + RaftEntity raft = new RaftEntity(boatData.raftEntity(), world, () -> TerraformWoodTest.customRaftItem); + raft.setPos(pos.getX() - 4, pos.getY(), pos.getZ()); world.spawnEntity(raft); - TerraformBoatEntity chestRaft = new TerraformBoatEntity(world, pos.getX() - 6, pos.getY(), pos.getZ()); - chestRaft.setTerraformBoat(raftType); + ChestRaftEntity chestRaft = new ChestRaftEntity(boatData.chestRaftEntity(), world, () -> TerraformWoodTest.customChestRaftItem); + chestRaft.setPos(pos.getX() - 6, pos.getY(), pos.getZ()); world.spawnEntity(chestRaft); // Spawn passengers diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/lang/en_us.json b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/lang/en_us.json new file mode 100644 index 00000000..5a67019c --- /dev/null +++ b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/lang/en_us.json @@ -0,0 +1,50 @@ +{ + "block.terraform.custom_log": "Custom Log", + "block.terraform.stripped_custom_log": "Stripped Custom Log", + "block.terraform.stripped_custom_wood": "Stripped Custom Wood", + "block.terraform.custom_wood": "Custom Wood", + "block.terraform.custom_leaves": "Custom Leaves", + "block.terraform.custom_sapling": "Custom Sapling", + "block.terraform.potted_custom_sapling": "Potted Custom Sapling", + "block.terraform.custom_planks": "Custom Planks", + "block.terraform.custom_stairs": "Custom Stairs", + "block.terraform.custom_slab": "Custom Slab", + "block.terraform.custom_sign": "Custom Sign", + "block.terraform.custom_hanging_sign": "Custom Hanging Sign", + "block.terraform.custom_button": "Custom Button", + "block.terraform.custom_pressure_plate": "Custom Pressure Plate", + "block.terraform.custom_fence": "Custom Fence", + "block.terraform.custom_fence_gate": "Custom Fence Gate", + "block.terraform.custom_door": "Custom Door", + "block.terraform.custom_trapdoor": "Custom Trapdoor", + + "entity.terraform.custom_boat": "Custom Boat", + "entity.terraform.custom_chest_boat": "Custom Boat with Chest", + "entity.terraform.custom_raft": "Custom Raft", + "entity.terraform.custom_chest_raft": "Custom Raft with Chest", + + "itemGroup.terraform.items": "Terraform Wood Test", + "item.terraform.custom_log": "Custom Log", + "item.terraform.stripped_custom_log": "Stripped Custom Log", + "item.terraform.stripped_custom_wood": "Stripped Custom Wood", + "item.terraform.custom_wood": "Custom Wood", + "item.terraform.custom_leaves": "Custom Leaves", + "item.terraform.custom_sapling": "Custom Sapling", + "item.terraform.potted_custom_sapling": "Potted Custom Sapling", + "item.terraform.custom_planks": "Custom Planks", + "item.terraform.custom_stairs": "Custom Stairs", + "item.terraform.custom_slab": "Custom Slab", + "item.terraform.custom_sign": "Custom Sign", + "item.terraform.custom_hanging_sign": "Custom Hanging Sign", + "item.terraform.custom_button": "Custom Button", + "item.terraform.custom_pressure_plate": "Custom Pressure Plate", + "item.terraform.custom_fence": "Custom Fence", + "item.terraform.custom_fence_gate": "Custom Fence Gate", + "item.terraform.custom_door": "Custom Door", + "item.terraform.custom_trapdoor": "Custom Trapdoor", + + "item.terraform.custom_boat": "Custom Boat", + "item.terraform.custom_chest_boat": "Custom Boat with Chest", + "item.terraform.custom_raft": "Custom Raft", + "item.terraform.custom_chest_raft": "Custom Raft with Chest" +} diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_chest_raft.json b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_chest_raft.json new file mode 100644 index 00000000..d3b4b792 --- /dev/null +++ b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_chest_raft.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "terraform:item/custom_chest_raft" + } +} diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_raft.json b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_raft.json new file mode 100644 index 00000000..986d615c --- /dev/null +++ b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/models/item/custom_raft.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "terraform:item/custom_raft" + } +} diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/boat/custom_boat.png b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/boat/custom.png similarity index 100% rename from terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/boat/custom_boat.png rename to terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/boat/custom.png diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_boat/custom_boat.png b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_boat/custom.png similarity index 100% rename from terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_boat/custom_boat.png rename to terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_boat/custom.png diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_raft/custom.png b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/entity/chest_raft/custom.png new file mode 100644 index 0000000000000000000000000000000000000000..ccf87085e63de02cc3308fc72393f255c2fa83f6 GIT binary patch literal 13724 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 WA~l)0v3~wbN9~rjQi;Od;Qs^LmL72c literal 0 HcmV?d00001 diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/item/custom_chest_raft.png b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/item/custom_chest_raft.png new file mode 100644 index 0000000000000000000000000000000000000000..71f48be66a45d31f25180897f94064217ef92f6d GIT binary patch literal 6680 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 literal 0 HcmV?d00001 diff --git a/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/item/custom_raft.png b/terraform-wood-api-v1/src/testmod/resources/assets/terraform/textures/item/custom_raft.png new file mode 100644 index 0000000000000000000000000000000000000000..71cc3d2e75b95e1d5ef6b59a21430ad1a85deb16 GIT binary patch literal 6629 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