diff --git a/build.gradle b/build.gradle index 27e333ade..5531051b7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { // see https://fabricmc.net/develop/ for new versions - id 'net.fabricmc.fabric-loom' version '1.15.5' apply false + id 'net.fabricmc.fabric-loom' version '1.17.17' apply false // see https://projects.neoforged.net/neoforged/moddevgradle for new versions - id 'net.neoforged.moddev' version '2.0.141' apply false + id 'net.neoforged.moddev' version '2.0.142' apply false id "me.modmuss50.mod-publish-plugin" version "1.1.0" apply false } \ No newline at end of file diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/acacia_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/acacia_branch.json index 4e352b493..6ed41cacd 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/acacia_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/acacia_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:acacia", "textures": { "bark": "minecraft:block/acacia_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/birch_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/birch_branch.json index e195b2b56..95fd5b705 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/birch_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/birch_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:birch", "textures": { "bark": "minecraft:block/birch_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/cherry_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/cherry_branch.json index 63a2e8b3b..bebb223f8 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/cherry_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/cherry_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:cherry", "textures": { "bark": "minecraft:block/cherry_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/crimson_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/crimson_branch.json index f6d350f23..75f25dbbc 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/crimson_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/crimson_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:crimson", "textures": { "bark": "minecraft:block/crimson_stem", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_branch.json index 8c7a4bee5..ccb326b6d 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:dark_oak", "textures": { "bark": "minecraft:block/dark_oak_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_root.json b/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_root.json index 7dd987b46..48a75cb95 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_root.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/dark_oak_root.json @@ -2,9 +2,10 @@ "variants": { "": { "type": "dynamictrees:surface_root", + "fabric:type": "dynamictrees:surface_root", "textures": { "bark": "minecraft:block/dark_oak_log" } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_branch.json index 9a5f34a4c..9c160cb08 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:jungle", "textures": { "bark": "minecraft:block/jungle_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_root.json b/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_root.json index c974dbf4a..10a0f2451 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_root.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/jungle_root.json @@ -2,9 +2,10 @@ "variants": { "": { "type": "dynamictrees:surface_root", + "fabric:type": "dynamictrees:surface_root", "textures": { "bark": "minecraft:block/jungle_log" } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_branch.json index 8fc27ad61..189cb16f0 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:mangrove", "textures": { "bark": "minecraft:block/mangrove_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_roots.json b/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_roots.json index 5a8c51b14..51d249f72 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_roots.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/mangrove_roots.json @@ -5,6 +5,7 @@ }, "layer=exposed": { "type": "dynamictrees:roots", + "fabric:type": "dynamictrees:roots", "opaque": false, "textures": { "side": "minecraft:block/mangrove_roots_side", @@ -13,6 +14,7 @@ }, "layer=filled": { "type": "dynamictrees:roots", + "fabric:type": "dynamictrees:roots", "opaque": true, "textures": { "side": "minecraft:block/muddy_mangrove_roots_side", @@ -20,4 +22,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/mossy_mangrove_roots.json b/common/src/generated/resources/assets/dynamictrees/blockstates/mossy_mangrove_roots.json index f127ee36d..e36541a23 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/mossy_mangrove_roots.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/mossy_mangrove_roots.json @@ -3,6 +3,7 @@ { "apply": { "type": "dynamictrees:roots", + "fabric:type": "dynamictrees:roots", "opaque": false, "textures": { "side": "minecraft:block/mangrove_roots_side", @@ -16,6 +17,7 @@ { "apply": { "type": "dynamictrees:roots", + "fabric:type": "dynamictrees:roots", "opaque": true, "textures": { "side": "minecraft:block/muddy_mangrove_roots_side", @@ -37,6 +39,7 @@ { "apply": { "type": "dynamictrees:roots_moss", + "fabric:type": "dynamictrees:roots_moss", "textures": { "moss": "minecraft:block/moss_block" } @@ -46,4 +49,4 @@ } } ] -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/oak_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/oak_branch.json index f3b48d6af..2595332e1 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/oak_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/oak_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:oak", "textures": { "bark": "minecraft:block/oak_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_branch.json index f7ede77a9..eb4a7bb43 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/pale_oak_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_creaking_heart_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_creaking_heart_branch.json index 04a02c417..d53334578 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_creaking_heart_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_creaking_heart_branch.json @@ -3,6 +3,7 @@ { "apply": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/pale_oak_log", @@ -16,6 +17,7 @@ { "apply": { "type": "dynamictrees:creaking_heart", + "fabric:type": "dynamictrees:creaking_heart", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/pale_oak_log", @@ -31,6 +33,7 @@ { "apply": { "type": "dynamictrees:creaking_heart", + "fabric:type": "dynamictrees:creaking_heart", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/pale_oak_log", @@ -46,6 +49,7 @@ { "apply": { "type": "dynamictrees:creaking_heart", + "fabric:type": "dynamictrees:creaking_heart", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/pale_oak_log", @@ -59,4 +63,4 @@ } } ] -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_root.json b/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_root.json index 388444ac2..84624835c 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_root.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/pale_oak_root.json @@ -2,9 +2,10 @@ "variants": { "": { "type": "dynamictrees:surface_root", + "fabric:type": "dynamictrees:surface_root", "textures": { "bark": "minecraft:block/pale_oak_log" } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/potted_sapling.json b/common/src/generated/resources/assets/dynamictrees/blockstates/potted_sapling.json index 5030d937f..fb9d8d810 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/potted_sapling.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/potted_sapling.json @@ -2,7 +2,8 @@ "variants": { "": { "type": "dynamictrees:potted_dynamic_sapling", + "fabric:type": "dynamictrees:potted_dynamic_sapling", "model": "minecraft:block/flower_pot" } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/resin_pale_oak_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/resin_pale_oak_branch.json index 82d1566c8..06d4bbd8a 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/resin_pale_oak_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/resin_pale_oak_branch.json @@ -3,6 +3,7 @@ { "apply": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/pale_oak_log", @@ -13,6 +14,7 @@ { "apply": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/resin_clump", @@ -21,4 +23,4 @@ } } ] -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/rooty_mangrove_aerial_roots.json b/common/src/generated/resources/assets/dynamictrees/blockstates/rooty_mangrove_aerial_roots.json index 91f364844..308102979 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/rooty_mangrove_aerial_roots.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/rooty_mangrove_aerial_roots.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:aerial_roots_soil", + "fabric:type": "dynamictrees:aerial_roots_soil", "family": "dynamictrees:mangrove", "textures": { "end": "minecraft:block/mangrove_log_top", @@ -11,4 +12,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/spruce_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/spruce_branch.json index 64dc4384e..f3a51ca47 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/spruce_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/spruce_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:spruce", "textures": { "bark": "minecraft:block/spruce_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_acacia_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_acacia_branch.json index a90a7ecac..54b2f1412 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_acacia_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_acacia_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:acacia", "textures": { "bark": "minecraft:block/stripped_acacia_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_birch_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_birch_branch.json index 4c57c2fbb..ea1d9b71d 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_birch_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_birch_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:birch", "textures": { "bark": "minecraft:block/stripped_birch_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_cherry_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_cherry_branch.json index ba7d0635f..5459a017f 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_cherry_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_cherry_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:cherry", "textures": { "bark": "minecraft:block/stripped_cherry_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_crimson_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_crimson_branch.json index 00c6b7049..de8ee8678 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_crimson_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_crimson_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:crimson", "textures": { "bark": "minecraft:block/stripped_crimson_stem", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_dark_oak_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_dark_oak_branch.json index bedb2a996..837e35f50 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_dark_oak_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_dark_oak_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:dark_oak", "textures": { "bark": "minecraft:block/stripped_dark_oak_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_jungle_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_jungle_branch.json index cf4ba940a..af9e713c6 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_jungle_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_jungle_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:jungle", "textures": { "bark": "minecraft:block/stripped_jungle_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_mangrove_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_mangrove_branch.json index 361f6ad11..18e818361 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_mangrove_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_mangrove_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:mangrove", "textures": { "bark": "minecraft:block/stripped_mangrove_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_oak_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_oak_branch.json index edfd98b36..285515531 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_oak_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_oak_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:oak", "textures": { "bark": "minecraft:block/stripped_oak_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_pale_oak_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_pale_oak_branch.json index 742fad84a..101658146 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_pale_oak_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_pale_oak_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:pale_oak", "textures": { "bark": "minecraft:block/stripped_pale_oak_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_spruce_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_spruce_branch.json index e0b2f21e0..e24d078dc 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_spruce_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_spruce_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:spruce", "textures": { "bark": "minecraft:block/stripped_spruce_log", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_warped_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_warped_branch.json index 4b4b50564..be59b71bb 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_warped_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/stripped_warped_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:warped", "textures": { "bark": "minecraft:block/stripped_warped_stem", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/generated/resources/assets/dynamictrees/blockstates/warped_branch.json b/common/src/generated/resources/assets/dynamictrees/blockstates/warped_branch.json index 6deee0565..660300534 100644 --- a/common/src/generated/resources/assets/dynamictrees/blockstates/warped_branch.json +++ b/common/src/generated/resources/assets/dynamictrees/blockstates/warped_branch.json @@ -2,6 +2,7 @@ "variants": { "": { "type": "dynamictrees:branch", + "fabric:type": "dynamictrees:branch", "family": "dynamictrees:warped", "textures": { "bark": "minecraft:block/warped_stem", @@ -9,4 +10,4 @@ } } } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/dtteam/dynamictrees/api/configuration/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/api/configuration/package-info.java deleted file mode 100644 index 1abc681aa..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/api/configuration/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.api.configuration; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/block/branch/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/block/branch/package-info.java deleted file mode 100644 index f9d4c45f8..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/block/branch/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.block.branch; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/block/leaves/DynamicLeavesBlock.java b/common/src/main/java/com/dtteam/dynamictrees/block/leaves/DynamicLeavesBlock.java index d3078af63..562b515ef 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/block/leaves/DynamicLeavesBlock.java +++ b/common/src/main/java/com/dtteam/dynamictrees/block/leaves/DynamicLeavesBlock.java @@ -31,7 +31,7 @@ import net.minecraft.util.Mth; import net.minecraft.util.ParticleUtils; import net.minecraft.util.RandomSource; -import net.minecraft.util.Tuple; +import com.mojang.datafixers.util.Pair; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.LivingEntity; @@ -175,15 +175,15 @@ public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource */ public boolean updateAllLeaves(LevelAccessor level, BlockPos startPos, BlockState startState, RandomSource rand, boolean worldGen){ //We store the position and hydro of the next block. - Queue> toProcess = new ArrayDeque<>(); + Queue> toProcess = new ArrayDeque<>(); Set processedPositions = new HashSet<>(); int firstHydro = updateHydro(level, startPos, startState, worldGen); - toProcess.add(new Tuple<>(startPos, firstHydro)); + toProcess.add(new Pair<>(startPos, firstHydro)); if (firstHydro == 0) return false; while (!toProcess.isEmpty() && processedPositions.size() <= getLeavesProperties().maxLeavesRecursion()){ - Tuple tup = toProcess.remove(); - BlockPos pos = tup.getA(); - int hydro = tup.getB(); + Pair tup = toProcess.remove(); + BlockPos pos = tup.getFirst(); + int hydro = tup.getSecond(); processedPositions.add(pos); for (Direction dir : Direction.values()) { // Go on all 6 sides of this block if (hydro > 1 || rand.nextInt(4) == 0) { // we'll give it a 1 in 4 chance to grow leaves if hydro is low to help performance @@ -201,7 +201,7 @@ public boolean updateAllLeaves(LevelAccessor level, BlockPos startPos, BlockStat //Do not iterate back through bigger hydro values //or if the leaves failed to grow if (sideHydro == 0 || sideHydro <= hydro){ - toProcess.add(new Tuple<>(sidePos, sideHydro)); + toProcess.add(new Pair<>(sidePos, sideHydro)); } } } diff --git a/common/src/main/java/com/dtteam/dynamictrees/block/leaves/LeavesProperties.java b/common/src/main/java/com/dtteam/dynamictrees/block/leaves/LeavesProperties.java index 4e6184a70..5dc35ffb2 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/block/leaves/LeavesProperties.java +++ b/common/src/main/java/com/dtteam/dynamictrees/block/leaves/LeavesProperties.java @@ -1,5 +1,9 @@ package com.dtteam.dynamictrees.block.leaves; +import net.minecraft.world.entity.EntityTypes; + +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.cell.CellKit; import com.dtteam.dynamictrees.api.registry.RegistryEntry; @@ -573,7 +577,7 @@ public BlockBehaviour.Properties getDefaultBlockProperties() { .randomTicks() .sound(SoundType.GRASS) .noOcclusion() - .isValidSpawn((s, r, p, e) -> e == EntityType.OCELOT || e == EntityType.PARROT) + .isValidSpawn((s, r, p, e) -> e == EntityTypes.OCELOT || e == EntityTypes.PARROT) .isSuffocating((s, r, p) -> false) .isViewBlocking((s, r, p) -> false) .isRedstoneConductor((s, r, p) -> false) @@ -710,13 +714,13 @@ public String toReloadDataString() { Pair.of("connectAnyRadius", this.connectAnyRadius)); } - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ getDynamicLeavesBlock().ifPresent(leaves -> defaultLeavesTags().forEach(tag -> { if (isOnlyIfLoaded()) { - tagAppender.apply(tag).addOptional(leaves); + tagAppender.apply(tag).addOptional(TagUtils.key(leaves)); } else { - tagAppender.apply(tag).add(leaves); + tagAppender.apply(tag).add(TagUtils.key(leaves)); } }) ); diff --git a/common/src/main/java/com/dtteam/dynamictrees/block/leaves/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/block/leaves/package-info.java deleted file mode 100644 index 8a2058796..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/block/leaves/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.block.leaves; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/block/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/block/package-info.java deleted file mode 100644 index 2242cf487..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/block/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.block; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/block/soil/SoilProperties.java b/common/src/main/java/com/dtteam/dynamictrees/block/soil/SoilProperties.java index 9c321b702..f8567d1fa 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/block/soil/SoilProperties.java +++ b/common/src/main/java/com/dtteam/dynamictrees/block/soil/SoilProperties.java @@ -1,5 +1,7 @@ package com.dtteam.dynamictrees.block.soil; +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.registry.RegistryEntry; import com.dtteam.dynamictrees.api.registry.RegistryHandler; @@ -294,14 +296,14 @@ public void setOnlyIfLoaded(String onlyIfLoaded) { this.onlyIfLoaded.add(onlyIfLoaded); } - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ // add rooty blocks to the rooty soil tag. getBlock().ifPresent(rootyBlock -> defaultSoilBlockTags().forEach(tag -> { if (!isOnlyIfLoaded()) { - tagAppender.apply(tag).add(rootyBlock); + tagAppender.apply(tag).add(TagUtils.key(rootyBlock)); } else { - tagAppender.apply(tag).addOptional(rootyBlock); + tagAppender.apply(tag).addOptional(TagUtils.key(rootyBlock)); } })); } diff --git a/common/src/main/java/com/dtteam/dynamictrees/block/soil/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/block/soil/package-info.java deleted file mode 100644 index 9dd2b758a..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/block/soil/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.block.soil; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/command/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/command/package-info.java deleted file mode 100644 index 200973484..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/command/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.command; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/data/BiGenerator.java b/common/src/main/java/com/dtteam/dynamictrees/data/BiGenerator.java index ff6dde83e..cfab352bf 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/data/BiGenerator.java +++ b/common/src/main/java/com/dtteam/dynamictrees/data/BiGenerator.java @@ -1,6 +1,6 @@ package com.dtteam.dynamictrees.data; -import net.minecraft.util.Tuple; +import com.mojang.datafixers.util.Pair; /** * A generator for a resource, providing the means to construct a Json file using provided {@link Dependencies} and @@ -10,11 +10,11 @@ * @param the type of the second generator * @param the type of the input to get the dependencies from */ -public interface BiGenerator extends Generator, I> { +public interface BiGenerator extends Generator, I> { @Override - default void generate(Tuple providers, I input, Dependencies dependencies){ - generate(providers.getA(), providers.getB(), input, dependencies); + default void generate(Pair providers, I input, Dependencies dependencies){ + generate(providers.getFirst(), providers.getSecond(), input, dependencies); } void generate(P1 provider1, P2 provider2, I input, Dependencies dependencies); diff --git a/common/src/main/java/com/dtteam/dynamictrees/data/DTLootTableBuilder.java b/common/src/main/java/com/dtteam/dynamictrees/data/DTLootTableBuilder.java index 63ba67c19..c633c7cd4 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/data/DTLootTableBuilder.java +++ b/common/src/main/java/com/dtteam/dynamictrees/data/DTLootTableBuilder.java @@ -8,7 +8,7 @@ import com.dtteam.dynamictrees.loot.function.MultiplyBySticksCount; import com.dtteam.dynamictrees.loot.function.MultiplyByTotalVolume; import com.dtteam.dynamictrees.utility.ItemUtils; -import net.minecraft.advancements.criterion.*; +import net.minecraft.advancements.predicates.*; import net.minecraft.core.HolderLookup; import net.minecraft.core.component.predicates.DataComponentPredicates; import net.minecraft.core.component.predicates.EnchantmentsPredicate; diff --git a/common/src/main/java/com/dtteam/dynamictrees/deserialization/deserializer/worldgen/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/deserialization/deserializer/worldgen/package-info.java deleted file mode 100644 index 2a15654db..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/deserialization/deserializer/worldgen/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.deserialization.deserializer.worldgen; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/deserialization/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/deserialization/package-info.java deleted file mode 100644 index cbec1edac..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/deserialization/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.deserialization; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/entity/FallingTreeEntity.java b/common/src/main/java/com/dtteam/dynamictrees/entity/FallingTreeEntity.java index 2d96262b6..8d6311716 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/entity/FallingTreeEntity.java +++ b/common/src/main/java/com/dtteam/dynamictrees/entity/FallingTreeEntity.java @@ -185,7 +185,7 @@ public void buildClient() { cleanupShellBlocks(destroyData); - Minecraft.getInstance().levelRenderer.setBlocksDirty(renderBounds.getMin().getX(), renderBounds.getMin().getY(), renderBounds.getMin().getZ(), renderBounds.getMax().getX(), renderBounds.getMax().getY(), renderBounds.getMax().getZ());//This forces the client to rerender the chunks + Minecraft.getInstance().levelExtractor.setBlocksDirty(renderBounds.getMin().getX(), renderBounds.getMin().getY(), renderBounds.getMin().getZ(), renderBounds.getMax().getX(), renderBounds.getMax().getY(), renderBounds.getMax().getZ());//This forces the client to rerender the chunks } protected void cleanupShellBlocks(BranchDestructionData destroyData) { diff --git a/common/src/main/java/com/dtteam/dynamictrees/entity/animation/FalloverAnimationHandler.java b/common/src/main/java/com/dtteam/dynamictrees/entity/animation/FalloverAnimationHandler.java index dbf41d0f0..be22de373 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/entity/animation/FalloverAnimationHandler.java +++ b/common/src/main/java/com/dtteam/dynamictrees/entity/animation/FalloverAnimationHandler.java @@ -191,7 +191,7 @@ private ParticleOptions getParticle(FallingTreeEntity entity, BlockState leavesS } protected void spawnParticlesAtLeaves(FallingTreeEntity entity, BlockPos leavesPos, ParticleOptions particle, Vec3 velocity, RandomSource rand, int particleCount, double limitChance){ - Vec3 newPos = getRelativeLeavesPosition(entity, leavesPos.getCenter()); + Vec3 newPos = getRelativeLeavesPosition(entity, Vec3.atCenterOf(leavesPos)); for (int j=0; j { + world.getBlockEntity(pos, BlockEntityTypes.BEEHIVE).ifPresent((blockEntity) -> { int j = 2 + random.nextInt(2); for(int k = 0; k < j; ++k) { diff --git a/common/src/main/java/com/dtteam/dynamictrees/systems/genfeature/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/systems/genfeature/package-info.java deleted file mode 100644 index 11080a1e3..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/systems/genfeature/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.systems.genfeature; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/systems/season/NormalSeasonManager.java b/common/src/main/java/com/dtteam/dynamictrees/systems/season/NormalSeasonManager.java index 560a68cfd..490cdefec 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/systems/season/NormalSeasonManager.java +++ b/common/src/main/java/com/dtteam/dynamictrees/systems/season/NormalSeasonManager.java @@ -6,7 +6,7 @@ import com.dtteam.dynamictrees.api.season.SeasonProvider; import net.minecraft.core.BlockPos; import net.minecraft.resources.Identifier; -import net.minecraft.util.Tuple; +import com.mojang.datafixers.util.Pair; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.biome.Biome; @@ -22,23 +22,23 @@ public class NormalSeasonManager implements SeasonManager { public static final Supplier NULL = NormalSeasonManager::new; private final Map seasonContextMap = new HashMap<>(); - private Function> seasonMapper = w -> new Tuple<>(new NullSeasonProvider(), new NullSeasonGrowthCalculator()); + private Function> seasonMapper = w -> new Pair<>(new NullSeasonProvider(), new NullSeasonGrowthCalculator()); public NormalSeasonManager() { } - public NormalSeasonManager(Function> seasonMapper) { + public NormalSeasonManager(Function> seasonMapper) { this.seasonMapper = seasonMapper; } - private Tuple createProvider(Level level) { + private Pair createProvider(Level level) { return seasonMapper.apply(level); } private SeasonContext getContext(Level level) { return seasonContextMap.computeIfAbsent(level.dimension().identifier(), d -> { - Tuple tuple = createProvider(level); - return new SeasonContext(tuple.getA(), tuple.getB()); + Pair tuple = createProvider(level); + return new SeasonContext(tuple.getFirst(), tuple.getSecond()); }); } diff --git a/common/src/main/java/com/dtteam/dynamictrees/tree/family/AerialRootsFamily.java b/common/src/main/java/com/dtteam/dynamictrees/tree/family/AerialRootsFamily.java index 3e31f75e4..cdbe15304 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/tree/family/AerialRootsFamily.java +++ b/common/src/main/java/com/dtteam/dynamictrees/tree/family/AerialRootsFamily.java @@ -1,5 +1,7 @@ package com.dtteam.dynamictrees.tree.family; +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.registry.TypedRegistry; import com.dtteam.dynamictrees.block.branch.BasicRootsBlock; @@ -214,16 +216,16 @@ public Optional getPrimitiveCoveredRoots() { } @Override - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ super.addGeneratedBlockTags(tagAppender); //Create roots tag and root harvest tag if the family is mangrove-like. getRoots().ifPresent(roots -> { - this.tierTag(getDefaultRootsHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(roots)); + this.tierTag(getDefaultRootsHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(TagUtils.key(roots))); defaultRootsTags().forEach(tag -> { if (!isOnlyIfLoaded()) { - tagAppender.apply(tag).add(roots); + tagAppender.apply(tag).add(TagUtils.key(roots)); } else { - tagAppender.apply(tag).addOptional(roots); + tagAppender.apply(tag).addOptional(TagUtils.key(roots)); } }); }); diff --git a/common/src/main/java/com/dtteam/dynamictrees/tree/family/AltBranchFamily.java b/common/src/main/java/com/dtteam/dynamictrees/tree/family/AltBranchFamily.java index 870c72d2d..6353aa36a 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/tree/family/AltBranchFamily.java +++ b/common/src/main/java/com/dtteam/dynamictrees/tree/family/AltBranchFamily.java @@ -1,5 +1,7 @@ package com.dtteam.dynamictrees.tree.family; +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.registry.TypedRegistry; import com.dtteam.dynamictrees.block.branch.BasicBranchBlock; @@ -110,15 +112,15 @@ public void addBranchTextures(BiConsumer textureConsumer, Id super.addBranchTextures(textureConsumer, primitiveLogLocation, sourceBlock); } - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ super.addGeneratedBlockTags(tagAppender); getAltBranch().ifPresent(branch -> { - tierTag(getDefaultBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(branch)); + tierTag(getDefaultBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(TagUtils.key(branch))); defaultBranchTags().forEach(tag -> { if (!isOnlyIfLoaded()) { - tagAppender.apply(tag).add(branch); + tagAppender.apply(tag).add(TagUtils.key(branch)); } else { - tagAppender.apply(tag).addOptional(branch); + tagAppender.apply(tag).addOptional(TagUtils.key(branch)); } }); }); diff --git a/common/src/main/java/com/dtteam/dynamictrees/tree/family/CreakingHeartFamily.java b/common/src/main/java/com/dtteam/dynamictrees/tree/family/CreakingHeartFamily.java index 2c07153a0..3b7ebf3bc 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/tree/family/CreakingHeartFamily.java +++ b/common/src/main/java/com/dtteam/dynamictrees/tree/family/CreakingHeartFamily.java @@ -1,5 +1,7 @@ package com.dtteam.dynamictrees.tree.family; +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.registry.TypedRegistry; import com.dtteam.dynamictrees.block.branch.*; @@ -113,15 +115,15 @@ public void addHeartTextures(BiConsumer textureConsumer, Ide } } - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ super.addGeneratedBlockTags(tagAppender); getHeartBranch().ifPresent(branch -> { - tierTag(getDefaultBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(branch)); + tierTag(getDefaultBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(TagUtils.key(branch))); defaultBranchTags().forEach(tag -> { if (!isOnlyIfLoaded()) { - tagAppender.apply(tag).add(branch); + tagAppender.apply(tag).add(TagUtils.key(branch)); } else { - tagAppender.apply(tag).addOptional(branch); + tagAppender.apply(tag).addOptional(TagUtils.key(branch)); } }); }); diff --git a/common/src/main/java/com/dtteam/dynamictrees/tree/family/Family.java b/common/src/main/java/com/dtteam/dynamictrees/tree/family/Family.java index a478eb678..def313158 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/tree/family/Family.java +++ b/common/src/main/java/com/dtteam/dynamictrees/tree/family/Family.java @@ -1,5 +1,7 @@ package com.dtteam.dynamictrees.tree.family; +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.registry.RegistryEntry; import com.dtteam.dynamictrees.api.registry.RegistryHandler; @@ -311,9 +313,11 @@ public boolean isWood() { } protected Optional getBranchBlock(int index) { + if (index < 0 || index >= branches.size()) return Optional.empty(); // Some families (e.g. from add-on tree packs) have no stripped branch entry. return Optionals.ofBlock(branches.get(index).getBlock()); } protected Optional getBranchItem(int index) { + if (index < 0 || index >= branches.size()) return Optional.empty(); return branches.get(index).getItem(); } @@ -715,33 +719,33 @@ public List> defaultStrippedBranchTags() { Collections.singletonList(DTBlockTags.STRIPPED_BRANCHES_THAT_BURN); } - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ getBranch().ifPresent(branch -> { - tierTag(getDefaultBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(branch)); + tierTag(getDefaultBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(TagUtils.key(branch))); defaultBranchTags().forEach(tag -> { if (!isOnlyIfLoaded()) { - tagAppender.apply(tag).add(branch); + tagAppender.apply(tag).add(TagUtils.key(branch)); } else { - tagAppender.apply(tag).addOptional(branch); + tagAppender.apply(tag).addOptional(TagUtils.key(branch)); } }); }); // Create stripped branch tag and harvest tag if the family has a stripped branch. getStrippedBranch().ifPresent(strippedBranch -> { - tierTag(getDefaultStrippedBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(strippedBranch)); + tierTag(getDefaultStrippedBranchHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(TagUtils.key(strippedBranch))); defaultStrippedBranchTags().forEach(tag -> { if (!isOnlyIfLoaded()) { - tagAppender.apply(tag).add(strippedBranch); + tagAppender.apply(tag).add(TagUtils.key(strippedBranch)); } else { - tagAppender.apply(tag).addOptional(strippedBranch); + tagAppender.apply(tag).addOptional(TagUtils.key(strippedBranch)); } }); }); } - protected Optional> tierTag(@Nullable ToolMaterial tier, Function, TagAppender> tagAppender) { + protected Optional> tierTag(@Nullable ToolMaterial tier, Function, TagAppender> tagAppender) { if (tier == null) return Optional.empty(); @@ -750,12 +754,12 @@ protected Optional> tierTag(@Nullable ToolMaterial tie return Optional.of(tagAppender.apply(tag)); } - public void addGeneratedItemTags (Function, TagAppender> tagAppender){ + public void addGeneratedItemTags (Function, TagAppender> tagAppender){ getBranchItem().ifPresent(item -> { if (!isOnlyIfLoaded()) { - defaultBranchItemTags().forEach(tag -> tagAppender.apply(tag).add(item)); + defaultBranchItemTags().forEach(tag -> tagAppender.apply(tag).add(TagUtils.key(item))); } else { - defaultBranchItemTags().forEach(tag -> tagAppender.apply(tag).addOptional(item)); + defaultBranchItemTags().forEach(tag -> tagAppender.apply(tag).addOptional(TagUtils.key(item))); } } ); diff --git a/common/src/main/java/com/dtteam/dynamictrees/tree/family/MossyAerialRootsFamily.java b/common/src/main/java/com/dtteam/dynamictrees/tree/family/MossyAerialRootsFamily.java index 9ecbcdcf5..4a2c5366c 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/tree/family/MossyAerialRootsFamily.java +++ b/common/src/main/java/com/dtteam/dynamictrees/tree/family/MossyAerialRootsFamily.java @@ -1,5 +1,7 @@ package com.dtteam.dynamictrees.tree.family; +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.registry.TypedRegistry; import com.dtteam.dynamictrees.block.branch.BasicRootsBlock; @@ -107,16 +109,16 @@ public List getBlockModelGenerators() { } @Override - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ super.addGeneratedBlockTags(tagAppender); //Create roots tag and root harvest tag if the family is mangrove-like. getMossyRoots().ifPresent(roots -> { - this.tierTag(getDefaultRootsHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(roots)); + this.tierTag(getDefaultRootsHarvestTier(), tagAppender).ifPresent(tagBuilder -> tagBuilder.add(TagUtils.key(roots))); defaultRootsTags().forEach(tag -> { if (!isOnlyIfLoaded()) { - tagAppender.apply(tag).add(roots); + tagAppender.apply(tag).add(TagUtils.key(roots)); } else { - tagAppender.apply(tag).addOptional(roots); + tagAppender.apply(tag).addOptional(TagUtils.key(roots)); } }); }); diff --git a/common/src/main/java/com/dtteam/dynamictrees/tree/species/Species.java b/common/src/main/java/com/dtteam/dynamictrees/tree/species/Species.java index adb8bc4be..0a059d87b 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/tree/species/Species.java +++ b/common/src/main/java/com/dtteam/dynamictrees/tree/species/Species.java @@ -1,5 +1,7 @@ package com.dtteam.dynamictrees.tree.species; +import com.dtteam.dynamictrees.utility.TagUtils; + import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.lazyvalue.LazyValue; import com.dtteam.dynamictrees.api.network.BranchDestructionData; @@ -2387,20 +2389,20 @@ public Identifier getSeedParentModelLocation() { return DynamicTrees.location("item/standard_seed"); } - public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ + public void addGeneratedBlockTags (Function, TagAppender> tagAppender){ // Create dynamic sapling block tags. getSapling().ifPresent(sapling -> defaultSaplingTags().forEach(tag -> { if (isOnlyIfLoaded()) { - tagAppender.apply(tag).addOptional(sapling); + tagAppender.apply(tag).addOptional(TagUtils.key(sapling)); } else { - tagAppender.apply(tag).add(sapling); + tagAppender.apply(tag).add(TagUtils.key(sapling)); } }) ); } - public void addGeneratedItemTags (Function, TagAppender> tagAppender){ + public void addGeneratedItemTags (Function, TagAppender> tagAppender){ // Some species return the common seed, so only return if the species has its own seed. if (!hasSeed()) { return; @@ -2409,9 +2411,9 @@ public void addGeneratedItemTags (Function, TagAppender getSeed().ifPresent(seed -> defaultSeedTags().forEach(tag ->{ if (isOnlyIfLoaded()) { - tagAppender.apply(tag).addOptional(seed); + tagAppender.apply(tag).addOptional(TagUtils.key(seed)); } else { - tagAppender.apply(tag).add(seed); + tagAppender.apply(tag).add(TagUtils.key(seed)); } }) ); diff --git a/common/src/main/java/com/dtteam/dynamictrees/treepack/package-info.java b/common/src/main/java/com/dtteam/dynamictrees/treepack/package-info.java deleted file mode 100644 index 0836ea062..000000000 --- a/common/src/main/java/com/dtteam/dynamictrees/treepack/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@MethodsReturnNonnullByDefault -package com.dtteam.dynamictrees.treepack; - -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; \ No newline at end of file diff --git a/common/src/main/java/com/dtteam/dynamictrees/utility/TagUtils.java b/common/src/main/java/com/dtteam/dynamictrees/utility/TagUtils.java new file mode 100644 index 000000000..5ecd81d71 --- /dev/null +++ b/common/src/main/java/com/dtteam/dynamictrees/utility/TagUtils.java @@ -0,0 +1,23 @@ +package com.dtteam.dynamictrees.utility; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +/** + * Helpers for converting registered objects to the {@link ResourceKey}s required by + * {@link net.minecraft.data.tags.TagAppender} since 26.2. + */ +public final class TagUtils { + + private TagUtils() {} + + public static ResourceKey key(Block block) { + return BuiltInRegistries.BLOCK.getResourceKey(block).orElseThrow(); + } + + public static ResourceKey key(Item item) { + return BuiltInRegistries.ITEM.getResourceKey(item).orElseThrow(); + } +} diff --git a/common/src/main/java/com/dtteam/dynamictrees/worldgen/featurecancellation/TreeFeatureCanceller.java b/common/src/main/java/com/dtteam/dynamictrees/worldgen/featurecancellation/TreeFeatureCanceller.java index 8c1e0b6b4..0c161117d 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/worldgen/featurecancellation/TreeFeatureCanceller.java +++ b/common/src/main/java/com/dtteam/dynamictrees/worldgen/featurecancellation/TreeFeatureCanceller.java @@ -47,8 +47,8 @@ but one (unclean) solution may be to add the non-tree features back to the gener private boolean doesContainTrees(RandomFeatureConfiguration featureConfig, BiomePropertySelectors.NormalFeatureCancellation featureCancellations) { - for (WeightedPlacedFeature feature : featureConfig.features) { - final PlacedFeature currentConfiguredFeature = feature.feature.value(); + for (WeightedPlacedFeature feature : featureConfig.features()) { + final PlacedFeature currentConfiguredFeature = feature.feature().value(); ConfiguredFeature cf = currentConfiguredFeature.getFeatures() .findFirst().map(Holder::value).orElse(null); if (cf == null) return false; diff --git a/common/src/main/java/com/dtteam/dynamictrees/worldgen/structure/TreePoolElement.java b/common/src/main/java/com/dtteam/dynamictrees/worldgen/structure/TreePoolElement.java index 132ef0d24..690d3de71 100644 --- a/common/src/main/java/com/dtteam/dynamictrees/worldgen/structure/TreePoolElement.java +++ b/common/src/main/java/com/dtteam/dynamictrees/worldgen/structure/TreePoolElement.java @@ -2,7 +2,6 @@ import com.dtteam.dynamictrees.registry.DTRegistries; import com.dtteam.dynamictrees.tree.species.Species; -import com.mojang.logging.annotations.MethodsReturnNonnullByDefault; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.BlockPos; @@ -30,7 +29,6 @@ /** * @author Harley O'Connor */ -@MethodsReturnNonnullByDefault public final class TreePoolElement extends StructurePoolElement { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance diff --git a/common/src/main/resources/META-INF/dynamictrees.accesswidener b/common/src/main/resources/META-INF/dynamictrees.accesswidener index 39ea50edd..e80d66106 100644 --- a/common/src/main/resources/META-INF/dynamictrees.accesswidener +++ b/common/src/main/resources/META-INF/dynamictrees.accesswidener @@ -7,23 +7,31 @@ accessible field net/minecraft/world/level/storage/loot/parameters/LootContextPa accessible method net/minecraft/world/level/levelgen/feature/stateproviders/BlockStateProviderType (Lcom/mojang/serialization/MapCodec;)V accessible field net/minecraft/world/level/levelgen/structure/pools/StructureTemplatePool rawTemplates Ljava/util/List; accessible field net/minecraft/world/level/levelgen/structure/pools/StructureTemplatePool templates Lit/unimi/dsi/fastutil/objects/ObjectArrayList; -accessible field net/minecraft/world/item/crafting/RecipeManager byType Lcom/google/common/collect/Multimap; -accessible field net/minecraft/world/item/Item craftingRemainingItem Lnet/minecraft/world/item/Item; -mutable field net/minecraft/world/item/Item craftingRemainingItem Lnet/minecraft/world/item/Item; +accessible method net/minecraft/world/item/Item getCraftingRemainder ()Lnet/minecraft/world/item/ItemStackTemplate; +extendable method net/minecraft/world/item/Item getCraftingRemainder ()Lnet/minecraft/world/item/ItemStackTemplate; +accessible field net/minecraft/world/item/Item craftingRemainingItem Lnet/minecraft/world/item/ItemStackTemplate; +mutable field net/minecraft/world/item/Item craftingRemainingItem Lnet/minecraft/world/item/ItemStackTemplate; accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z -accessible field net/minecraft/world/level/levelgen/feature/stateproviders/WeightedStateProvider weightedList Lnet/minecraft/util/random/SimpleWeightedRandomList; +accessible field net/minecraft/world/level/levelgen/feature/stateproviders/WeightedStateProvider weightedList Lnet/minecraft/util/random/WeightedList; accessible field net/minecraft/commands/synchronization/ArgumentTypeInfos BY_CLASS Ljava/util/Map; accessible class net/minecraft/world/inventory/BrewingStandMenu$PotionSlot -accessible class net/minecraft/data/tags/IntrinsicHolderTagsProvider$IntrinsicTagAppender -accessible method net/minecraft/client/renderer/texture/SpriteContents metadata ()Lnet/minecraft/server/packs/resources/ResourceMetadata; accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage; -accessible method net/minecraft/client/renderer/texture/atlas/SpriteSources register (Ljava/lang/String;Lcom/mojang/serialization/MapCodec;)Lnet/minecraft/client/renderer/texture/atlas/SpriteSourceType; +accessible field net/minecraft/client/renderer/texture/SpriteContents additionalMetadata Ljava/util/List; accessible method net/minecraft/client/data/models/model/TextureSlot create (Ljava/lang/String;)Lnet/minecraft/client/data/models/model/TextureSlot; +accessible field net/minecraft/world/level/block/entity/BlockEntity type Lnet/minecraft/world/level/block/entity/BlockEntityType; mutable field net/minecraft/world/level/block/entity/BlockEntity type Lnet/minecraft/world/level/block/entity/BlockEntityType; -mutable field net/minecraft/world/level/block/entity/BlockEntity blockState Lnet/minecraft/world/level/block/state/BlockState; \ No newline at end of file +accessible field net/minecraft/world/level/block/entity/BlockEntity blockState Lnet/minecraft/world/level/block/state/BlockState; +mutable field net/minecraft/world/level/block/entity/BlockEntity blockState Lnet/minecraft/world/level/block/state/BlockState; + +extendable method net/minecraft/world/level/block/entity/CreakingHeartBlockEntity spreadResin (Lnet/minecraft/server/level/ServerLevel;)Ljava/util/Optional; +accessible field net/minecraft/world/level/block/entity/CreakingHeartBlockEntity ticksExisted J + +accessible field net/minecraft/client/renderer/block/dispatch/multipart/MultiPartModel models Ljava/util/List; + +accessible field net/minecraft/client/color/item/ItemTintSources ID_MAPPER Lnet/minecraft/util/ExtraCodecs$LateBoundIdMapper; diff --git a/fabric/build.gradle b/fabric/build.gradle index 55794b978..ecae446b8 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -5,10 +5,45 @@ plugins { //mod_version = "${mod_version}${fabricVersionAppend}" version = mod_version +repositories { + exclusiveContent { + forRepository { + maven { + name = 'Modrinth' + url = 'https://api.modrinth.com/maven' + } + } + filter { + includeGroup 'maven.modrinth' + } + } +} + dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" implementation "net.fabricmc:fabric-loader:${fabric_loader_version}" implementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" + compileOnly "maven.modrinth:jade:26.2.9+fabric" + compileOnly "maven.modrinth:continuity:3.0.1+26.2" + compileOnly "maven.modrinth:serene-seasons:26.1.2.0.4" + compileOnly "maven.modrinth:glitchcore:26.2.0.0.0" // transitive API of serene-seasons, needed at compile time + implementation "fuzs.forgeconfigapiport:forgeconfigapiport-fabric:${project.forge_config_api_port_version}" + compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2' // javax.annotation used by common sources +} + +// Include the datagenned assets/data (blockstates, item/block models, loot, ...) like the neoforge jar does. +sourceSets.main.resources { srcDir project(':common').file('src/generated/resources') } +// The generated directory can be seen twice by the copy spec (identical files); first one wins. +tasks.named('processResources', ProcessResources) { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } +tasks.named('sourcesJar', Jar) { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } + +// Ship the generated data (tags, loot tables) in the fabric jar too. The generated assets +// (blockstates using the custom "dynamictrees:branch" model type) stay neoforge-only — +// the fabric model layer replaces branch models after bake and cannot parse them. +processResources { + from(project(':common').file('src/generated/resources')) { + include 'data/**' + } } loom { diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabric.java b/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabric.java index 7023e005b..4d2dc59dd 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabric.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabric.java @@ -9,7 +9,7 @@ import com.dtteam.dynamictrees.registry.FabricRegistryHandler; import com.dtteam.dynamictrees.registry.FabricRegistryLoader; import com.dtteam.dynamictrees.worldgen.FabricBiomeModifications; -import fuzs.forgeconfigapiport.fabric.api.neoforge.v4.*; +import fuzs.forgeconfigapiport.fabric.api.v5.ConfigRegistry; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.loader.api.FabricLoader; @@ -21,8 +21,8 @@ public class DynamicTreesFabric implements ModInitializer { @Override public void onInitialize() { - NeoForgeConfigRegistry.INSTANCE.register(DynamicTrees.MOD_ID,ModConfig.Type.SERVER, DTConfigs.SERVER_CONFIG); - NeoForgeConfigRegistry.INSTANCE.register(DynamicTrees.MOD_ID,ModConfig.Type.COMMON, DTConfigs.COMMON_CONFIG); + ConfigRegistry.INSTANCE.register(DynamicTrees.MOD_ID, ModConfig.Type.SERVER, DTConfigs.SERVER_CONFIG); + ConfigRegistry.INSTANCE.register(DynamicTrees.MOD_ID, ModConfig.Type.COMMON, DTConfigs.COMMON_CONFIG); FabricRegistryHandler.setup(DynamicTrees.MOD_ID); @@ -44,13 +44,19 @@ public void onInitialize() { FabricRegistryLoader.setup(); - DynamicTrees.commonSetup(); + // Note: DynamicTrees.commonSetup() is deliberately NOT called here. It applies the + // treepack "setup" stage (primitive log/leaves/stripped log block lookups etc.), which + // must run after ALL mods have registered their blocks. Fabric invokes ModInitializers + // in an order that may put this mod before content mods (e.g. Biomes O' Plenty), which + // previously made every add-on treepack primitive lookup fail on the first pass. + // It is instead called from the client/dedicated-server entrypoints, which Fabric runs + // after the whole main entrypoint phase (mirroring NeoForge's FMLCommonSetupEvent + // timing). See DynamicTreesFabricClient and DynamicTreesFabricServer. FabricBiomeModifications.register(); ServerLifecycleEvents.SERVER_STARTING.register(server -> { VanillaSaplingEventHandler.updateEnabled(); - FabricMiscHelper.debugSpeciesRegistry(); }); ServerLifecycleEvents.SERVER_STOPPING.register(server -> { diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricClient.java b/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricClient.java index 4587c2371..70cfd83ba 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricClient.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricClient.java @@ -6,6 +6,7 @@ import com.dtteam.dynamictrees.block.sapling.*; import com.dtteam.dynamictrees.block.soil.*; import com.dtteam.dynamictrees.client.*; +import com.dtteam.dynamictrees.client.TintSources.*; import com.dtteam.dynamictrees.config.*; import com.dtteam.dynamictrees.item.*; import com.dtteam.dynamictrees.model.*; @@ -16,15 +17,20 @@ import com.dtteam.dynamictrees.tree.*; import com.dtteam.dynamictrees.tree.family.*; import com.dtteam.dynamictrees.tree.species.*; -import fuzs.forgeconfigapiport.fabric.api.neoforge.v4.*; +import fuzs.forgeconfigapiport.fabric.api.v5.ConfigRegistry; import net.fabricmc.api.*; -import net.fabricmc.fabric.api.blockrenderlayer.v1.*; import net.fabricmc.fabric.api.client.event.lifecycle.v1.*; import net.fabricmc.fabric.api.client.item.v1.*; import net.fabricmc.fabric.api.client.model.loading.v1.*; import net.fabricmc.fabric.api.client.rendering.v1.*; import net.minecraft.client.*; +import net.minecraft.client.color.block.BlockTintSource; +import net.minecraft.client.color.block.BlockTintSources; +import net.minecraft.client.color.item.ItemTintSources; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; import net.minecraft.client.renderer.texture.*; +import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.core.*; import net.minecraft.resources.*; import net.minecraft.util.*; @@ -46,11 +52,17 @@ public class DynamicTreesFabricClient implements ClientModInitializer { @Override public void onInitializeClient() { - NeoForgeConfigRegistry.INSTANCE.register(DynamicTrees.MOD_ID, ModConfig.Type.CLIENT, DTConfigs.CLIENT_CONFIG); - AtlasSourceTypeRegistryImpl.register(ThickBranchRingsSource.ID, ThickBranchRingsSource.setType(ThickBranchRingsSource.CODEC)); + // Runs after ALL main entrypoints, so every mod's blocks are registered and the + // treepack setup stage (primitive log/leaves lookups) resolves add-on blocks. + // This must happen before the first resource load so models bake with correct data. + DynamicTrees.commonSetup(); + + ConfigRegistry.INSTANCE.register(DynamicTrees.MOD_ID, ModConfig.Type.CLIENT, DTConfigs.CLIENT_CONFIG); + SpriteSourceRegistry.register(ThickBranchRingsSource.ID, ThickBranchRingsSource.CODEC); registerModelLoaders(); registerEntityRenderers(); registerColorHandlers(); + registerItemTintSources(); registerTooltipCallback(); registerClientTick(); registerClientWorldLoad(); @@ -61,15 +73,15 @@ private void registerClientWorldLoad() { ClientTickEvents.START_CLIENT_TICK.register(client -> { if (!initialized && client.level != null) { discoverWoodColors(); - LeavesProperties.postInitClient(); - BlockColorMultipliers.cleanUp(); registerBlockColors(); + BlockColorMultipliers.cleanUp(); initialized = true; } }); } private void registerModelLoaders() { + DTModelLoadingPlugin.registerModelTypes(); ModelLoadingPlugin.register(new DTModelLoadingPlugin()); } @@ -79,78 +91,62 @@ private void registerEntityRenderers() { } private void registerColorHandlers() { - BlockColorMultipliers.register("birch", (state, level, pos, tintIndex) -> FoliageColor.getBirchColor()); - BlockColorMultipliers.register("spruce", (state, level, pos, tintIndex) -> FoliageColor.getEvergreenColor()); + BlockColorMultipliers.register("birch", BlockTintSources.constant(FoliageColor.FOLIAGE_BIRCH)); + BlockColorMultipliers.register("spruce", BlockTintSources.constant(FoliageColor.FOLIAGE_EVERGREEN)); + } - ColorProviderRegistry.ITEM.register(DTRegistries.DENDRO_POTION.get()::getColor, DTRegistries.DENDRO_POTION.get()); - ColorProviderRegistry.ITEM.register(DTRegistries.STAFF.get()::getColor, DTRegistries.STAFF.get()); + private void registerItemTintSources() { + // Mirrors the NeoForge RegisterColorHandlersEvent.ItemTintSources registrations; the + // item model JSONs reference these tint source types by id. + ItemTintSources.ID_MAPPER.put(DynamicTrees.location("dendro_potion"), DendroPotionItemTintSource.MAP_CODEC); + ItemTintSources.ID_MAPPER.put(DynamicTrees.location("staff_handle"), StaffHandleItemTintSource.MAP_CODEC); + ItemTintSources.ID_MAPPER.put(DynamicTrees.location("staff_crystal"), StaffCrystalItemTintSource.MAP_CODEC); } public static void registerBlockColors() { - final int white = 0xFFFFFFFF; - final int magenta = 0x00FF00FF; final var blockColors = Minecraft.getInstance().getBlockColors(); - for (SoilProperties soil : SoilProperties.REGISTRY) { - if (soil.getBlock().isEmpty()) continue; - SoilBlock roots = soil.getBlock().get(); - ColorProviderRegistry.BLOCK.register( - (state, level, pos, tintIndex) -> roots.colorMultiplier(blockColors, state, level, pos, tintIndex), - roots - ); - BlockRenderLayerMap.INSTANCE.putBlock(roots, RenderType.cutoutMipped()); - } - - for (Family family : Family.REGISTRY.getAll()) { - if (family instanceof AerialRootsFamily rootsFamily) { - rootsFamily.getRoots().ifPresent(roots -> - BlockRenderLayerMap.INSTANCE.putBlock(roots, RenderType.cutoutMipped()) - ); - } - } - - - ColorProviderRegistry.BLOCK.register( - (state, level, pos, tintIndex) -> isValidPos(level, pos) && (state.getBlock() instanceof PottedSaplingBlock) - ? DTRegistries.POTTED_SAPLING.get().getSpecies(level, pos).saplingColorMultiplier(state, level, pos, tintIndex) : white, - DTRegistries.POTTED_SAPLING.get() - ); - - for (Species species : Species.REGISTRY) { - if (species.getSapling().isPresent()) { - ColorProviderRegistry.BLOCK.register( - (state, level, pos, tintIndex) -> isValidPos(level, pos) - ? species.saplingColorMultiplier(state, level, pos, tintIndex) : white, - species.getSapling().get() - ); - } - species.getSapling().ifPresent(sapling -> BlockRenderLayerMap.INSTANCE.putBlock(sapling, RenderType.cutoutMipped())); - if(species.hasFruits()){ - species.getFruits().forEach(fruit -> - BlockRenderLayerMap.INSTANCE.putBlock(fruit.getBlock(), RenderType.cutoutMipped()) - ); - } - if(species.hasPods()){ - species.getPods().forEach(pod -> - BlockRenderLayerMap.INSTANCE.putBlock(pod.getBlock(), RenderType.cutoutMipped()) - ); - } - } + // Register Rooty Soils Tint Sources + SoilProperties.REGISTRY.getAll().stream().map(SoilProperties::getBlock).flatMap(Optional::stream) + .forEach(soilBlock -> { + SoilProperties properties = soilBlock.getSoilProperties(); + List sources = CloneTintSource.cloneAllSources( + blockColors, + () -> properties.getPrimitiveSoilState(soilBlock.defaultBlockState()), + properties.getFoliageTintLayerCount()); + sources.add(new SoilRootsTintSource(soilBlock)); + BlockColorRegistry.register(sources, soilBlock); + }); - for (DynamicLeavesBlock leaves : LeavesProperties.REGISTRY.getAll().stream() - .filter(lp -> lp.getDynamicLeavesBlock().isPresent()) - .map(lp -> lp.getDynamicLeavesBlock().get()) - .collect(Collectors.toSet())) { - ColorProviderRegistry.BLOCK.register( - (state, level, pos, tintIndex) -> isValidPos(level, pos) && TreeHelper.isLeaves(state.getBlock()) - ? ((DynamicLeavesBlock) state.getBlock()).getLeavesProperties().foliageColorMultiplier(state, level, pos) : magenta, - leaves - ); - } - } + // Register Leaves Tint Sources + LeavesProperties.REGISTRY.getAll().stream().map(LeavesProperties::getDynamicLeavesBlock).flatMap(Optional::stream) + .forEach(leaves -> { + LeavesProperties properties = leaves.getLeavesProperties(); + if (properties.hasCustomColor()) { + Integer customColor = properties.getCustomColor(); + if (customColor == null) //we use null as a way to default back to "biome" index source. + BlockColorRegistry.register(List.of(BlockTintSources.foliage()), leaves); + else + BlockColorRegistry.register(List.of(BlockTintSources.constant(customColor)), leaves); + } else { + BlockColorRegistry.register(CloneTintSource.cloneAllSources(blockColors, properties::getPrimitiveLeaves, properties.getFoliageTintLayerCount()), leaves); + } + }); - private static boolean isValidPos(BlockGetter level, BlockPos pos) { - return level != null && pos != null; + // Register Potted Sapling Tint Sources + BlockColorRegistry.register(List.of(new PottedSaplingTintSource(blockColors)), DTRegistries.POTTED_SAPLING.get()); + + // Register Sapling Tint Sources + Species.REGISTRY.getAll().stream().map(Species::getSapling).flatMap(Optional::stream) + .forEach(sapling -> { + Species species = sapling.getSpecies(); + BlockColorRegistry.register(List.of( + //Leaves for the leaves + new SaplingTintSource(blockColors, species), + //Mangrove saplings have roots so we provide a branch tint + new SuppliedConstantTintSource(() -> species.getFamily().woodBarkColor) + ), sapling); + }); } private void registerTooltipCallback() { @@ -182,43 +178,30 @@ private void registerTooltipCallback() { } private void registerClientTick() { - ClientTickEvents.START_WORLD_TICK.register(level -> { - SeasonHelper.updateTick(level, level.getDayTime()); + ClientTickEvents.START_LEVEL_TICK.register(level -> { + SeasonHelper.updateTick(level, level.getDefaultClockTime()); }); } public static void discoverWoodColors() { - final Function bakedTextureGetter = Minecraft.getInstance() - .getTextureAtlas(InventoryMenu.BLOCK_ATLAS); - for (Family family : Family.REGISTRY.getAll()) { family.woodRingColor = 0xFFF1AE; family.woodBarkColor = 0xB3A979; if (family != Family.NULL_FAMILY) { family.getPrimitiveLog().ifPresent(branch -> { BlockState state = branch.defaultBlockState(); - family.woodRingColor = getFaceColor(state, Direction.DOWN, bakedTextureGetter); - family.woodBarkColor = getFaceColor(state, Direction.NORTH, bakedTextureGetter); + family.woodRingColor = getFaceColor(state, Direction.DOWN); + family.woodBarkColor = getFaceColor(state, Direction.NORTH); }); } } } - private static int getFaceColor(BlockState state, Direction face, Function textureGetter) { - final BakedModel model = Minecraft.getInstance().getBlockRenderer().getBlockModel(state); - if (model == null) { - DynamicTrees.LOG.warn("Could not get model for {}! Branch needs to be handled manually!", state.getBlock()); - return 0; - } - List quads = model.getQuads(state, face, RandomSource.create()); - if (quads.isEmpty()) { - quads = model.getQuads(state, null, RandomSource.create()); - } - if (quads.isEmpty()) { - DynamicTrees.LOG.warn("Could not get color of {} side for {}! Branch needs to be handled manually!", face, state.getBlock()); - return 0; - } - TextureAtlasSprite sprite = quads.getFirst().getSprite(); + private static int getFaceColor(BlockState state, Direction face) { + final BlockStateModel model = Minecraft.getInstance().getModelManager().getBlockStateModelSet().get(state); + List quads = getQuads(state, face, model); + if (quads == null) return 0; + TextureAtlasSprite sprite = quads.getFirst().materialInfo().sprite(); final TextureHelper.PixelBuffer pixelBuffer = new TextureHelper.PixelBuffer(sprite); final int u = pixelBuffer.w / 16; final TextureHelper.PixelBuffer center = new TextureHelper.PixelBuffer(u * 8, u * 8); @@ -226,4 +209,25 @@ private static int getFaceColor(BlockState state, Direction face, Function getQuads(BlockState state, Direction face, BlockStateModel model) { + List parts = new ArrayList<>(); + model.collectParts(RandomSource.create(), parts); + + if (parts.isEmpty()) { // No parts? empty model + DynamicTrees.LOG.warn("Could not get any color from {}, model is empty! Branch color needs to be handled manually.", state.getBlock()); + return null; + } + //We only care about the first, we assume these are all regular blocks + List quads = parts.getFirst().getQuads(face); + if (quads.isEmpty()) // If the quad list is empty, means there is no face on that side, so we try with null. + { + quads = parts.getFirst().getQuads(null); + } + if (quads.isEmpty()) { // If null still returns empty, there is nothing we can do so we just warn and exit. + DynamicTrees.LOG.warn("Could not get color of {} side for {}! Branch color needs to be handled manually.", face, state.getBlock()); + return null; + } + return quads; + } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricServer.java b/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricServer.java new file mode 100644 index 000000000..d280ee3a7 --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricServer.java @@ -0,0 +1,17 @@ +package com.dtteam.dynamictrees; + +import net.fabricmc.api.DedicatedServerModInitializer; + +/** + * Dedicated server entrypoint. Fabric invokes this after the whole main entrypoint phase, + * so every mod's blocks are registered by the time the treepack setup stage (primitive + * log/leaves lookups etc.) runs — mirroring NeoForge's FMLCommonSetupEvent timing. + * The client counterpart is {@link DynamicTreesFabricClient}. + */ +public class DynamicTreesFabricServer implements DedicatedServerModInitializer { + + @Override + public void onInitializeServer() { + DynamicTrees.commonSetup(); + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/compat/SereneSeasonsSeasonProvider.java b/fabric/src/main/java/com/dtteam/dynamictrees/compat/SereneSeasonsSeasonProvider.java index 58a05eb44..1519d6894 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/compat/SereneSeasonsSeasonProvider.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/compat/SereneSeasonsSeasonProvider.java @@ -3,8 +3,8 @@ import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.api.season.SeasonProvider; import com.dtteam.dynamictrees.systems.season.*; +import com.mojang.datafixers.util.Pair; import net.minecraft.core.BlockPos; -import net.minecraft.util.Tuple; import net.minecraft.world.level.Level; import sereneseasons.api.season.Season; import sereneseasons.init.ModConfig; @@ -26,7 +26,7 @@ public void updateTick(Level level, long dayTime) { @Override public boolean shouldSnowMelt(Level level, BlockPos pos) { if (ModConfig.seasons.generateSnowAndIce && seasonValue < SeasonHelper.WINTER_START) { - return level.getBiome(pos).value().warmEnoughToRain(pos); + return level.getBiome(pos).value().warmEnoughToRain(pos, level.getSeaLevel()); } return false; } @@ -35,8 +35,8 @@ public static void registerSereneSeasonsProvider (){ SeasonCompatibilityHandler.registerSeasonManager(DynamicTrees.SERENE_SEASONS, () -> { NormalSeasonManager seasonManager = new NormalSeasonManager( world -> ModConfig.seasons.isDimensionWhitelisted(world.dimension()) ? - new Tuple<>(new SereneSeasonsSeasonProvider(), new ActiveSeasonGrowthCalculator()) : - new Tuple<>(new NullSeasonProvider(), new NullSeasonGrowthCalculator()) + new Pair<>(new SereneSeasonsSeasonProvider(), new ActiveSeasonGrowthCalculator()) : + new Pair<>(new NullSeasonProvider(), new NullSeasonGrowthCalculator()) ); seasonManager.setTropicalPredicate((world, pos) -> sereneseasons.api.season.SeasonHelper.usesTropicalSeasons(world.getBiome(pos))); return seasonManager; diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/ContinuityWrappedModelHandler.java b/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/ContinuityWrappedModelHandler.java index d357eeba8..47310c4b1 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/ContinuityWrappedModelHandler.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/ContinuityWrappedModelHandler.java @@ -1,16 +1,35 @@ package com.dtteam.dynamictrees.compat.continuity; import com.dtteam.dynamictrees.model.baked.BasicBranchBlockBakedModel; -import me.pepperbell.continuity.client.model.CtmBakedModel; -import net.minecraft.client.resources.model.BakedModel; +import me.pepperbell.continuity.client.model.CtmBlockStateModel; +import net.fabricmc.fabric.api.client.model.loading.v1.wrapper.WrapperBlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; import org.jetbrains.annotations.Nullable; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + public class ContinuityWrappedModelHandler extends WrappedModelHandler { + // Continuity's CtmBlockStateModel extends Fabric's WrapperBlockStateModel, whose + // wrapped model field is protected with no public accessor. + @Nullable + private static final VarHandle WRAPPED_FIELD = findWrappedField(); + + @Nullable + private static VarHandle findWrappedField() { + try { + return MethodHandles.privateLookupIn(WrapperBlockStateModel.class, MethodHandles.lookup()) + .findVarHandle(WrapperBlockStateModel.class, "wrapped", BlockStateModel.class); + } catch (ReflectiveOperationException e) { + return null; + } + } + @Override - public @Nullable BasicBranchBlockBakedModel unwrapBranchModel(BakedModel model) { - if (model instanceof CtmBakedModel ctmModel){ - return super.unwrapBranchModel(ctmModel.getWrappedModel()); + public @Nullable BasicBranchBlockBakedModel unwrapBranchModel(BlockStateModel model) { + if (model instanceof CtmBlockStateModel ctmModel && WRAPPED_FIELD != null) { + return super.unwrapBranchModel((BlockStateModel) WRAPPED_FIELD.get(ctmModel)); } return super.unwrapBranchModel(model); } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/WrappedModelHandler.java b/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/WrappedModelHandler.java index 36ac0d73d..0ec9d895c 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/WrappedModelHandler.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/compat/continuity/WrappedModelHandler.java @@ -2,16 +2,16 @@ import com.dtteam.dynamictrees.model.baked.BasicBranchBlockBakedModel; import com.dtteam.dynamictrees.platform.Services; -import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; import org.jetbrains.annotations.Nullable; public abstract class WrappedModelHandler { private static WrappedModelHandler INSTANCE = null; - public static WrappedModelHandler getInstance(){ + public static WrappedModelHandler getInstance() { if (INSTANCE == null) { - if (Services.PLATFORM.isModLoaded("continuity")){ + if (Services.PLATFORM.isModLoaded("continuity")) { INSTANCE = new ContinuityWrappedModelHandler(); } else { INSTANCE = new WrappedModelHandler() {}; @@ -21,9 +21,10 @@ public static WrappedModelHandler getInstance(){ } @Nullable - public BasicBranchBlockBakedModel unwrapBranchModel(BakedModel model){ - if (model instanceof BasicBranchBlockBakedModel branchModel) + public BasicBranchBlockBakedModel unwrapBranchModel(BlockStateModel model) { + if (model instanceof BasicBranchBlockBakedModel branchModel) { return branchModel; + } return null; } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/compat/waila/WailaBranchHandler.java b/fabric/src/main/java/com/dtteam/dynamictrees/compat/waila/WailaBranchHandler.java index 89556b3fe..a66504ff3 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/compat/waila/WailaBranchHandler.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/compat/waila/WailaBranchHandler.java @@ -28,8 +28,6 @@ import snownee.jade.api.IBlockComponentProvider; import snownee.jade.api.ITooltip; import snownee.jade.api.config.IPluginConfig; -import snownee.jade.api.ui.IElement; -import snownee.jade.impl.ui.ElementHelper; import java.util.LinkedList; import java.util.List; @@ -57,7 +55,7 @@ public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfi //Attempt to get species from server via NBT data if (nbtData.contains("species")) { - species = Species.findSpecies(Identifier.parse(nbtData.getString("species"))); + species = Species.findSpecies(Identifier.parse(nbtData.getString("species").get())); } //Attempt to get species by checking if we're still looking at the same block @@ -91,47 +89,49 @@ public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfi tooltip.add(Component.literal(ChatFormatting.DARK_GRAY + species.getRegistryName().toString())); } - ItemStack seedStack = species.getSeedStack(1); - - List elements = new LinkedList<>(); - elements.add(getElement(seedStack)); //adds seed; - - if (species.hasFruits()){ - for (Fruit fruit : species.getFruits()){ - ItemStack fruitStack = fruit.getItemStack(); - if (fruitStack.getItem() != seedStack.getItem()) - elements.add(getElement(fruitStack)); - } - } - if (species.hasPods()){ - for (Pod pod : species.getPods()){ - ItemStack podStack = pod.getItemStack(); - if (podStack.getItem() != seedStack.getItem()) - elements.add(getElement(podStack)); - } - } - - int silkTouch = ItemUtils.getEnchantmentLevel(Enchantments.SILK_TOUCH, accessor.getPlayer().getMainHandItem(), accessor.getPlayer().registryAccess()); - int fortune = ItemUtils.getEnchantmentLevel(Enchantments.FORTUNE, accessor.getPlayer().getMainHandItem(), accessor.getPlayer().registryAccess()); - if (lastVolume.getVolume() > 0) { - LogsAndSticks las = species.getLogsAndSticks(lastVolume, silkTouch > 0, fortune); - List logStacks = las.logs; - if (!logStacks.isEmpty()) { - for (ItemStack logStack : logStacks) { - elements.add(getElement(logStack)); - } - } - if (las.sticks > 0) { - ItemStack stickStack = species.getFamily().getStickStack(las.sticks); - if (!stickStack.isEmpty()) { - elements.add(getElement(stickStack)); - } - } - } - - tooltip.add(elements.removeFirst()); - elements.forEach(tooltip::append); - tooltip.add(ElementHelper.INSTANCE.spacer(0, 2)); + // TODO 26.2 port: Jade removed the IElement/ElementHelper API; the item icon row + // (seed/fruit/pod/log/stick stacks) needs to be reimplemented against the new Jade API. +// ItemStack seedStack = species.getSeedStack(1); +// +// List elements = new LinkedList<>(); +// elements.add(getElement(seedStack)); //adds seed; +// +// if (species.hasFruits()){ +// for (Fruit fruit : species.getFruits()){ +// ItemStack fruitStack = fruit.getItemStack(); +// if (fruitStack.getItem() != seedStack.getItem()) +// elements.add(getElement(fruitStack)); +// } +// } +// if (species.hasPods()){ +// for (Pod pod : species.getPods()){ +// ItemStack podStack = pod.getItemStack(); +// if (podStack.getItem() != seedStack.getItem()) +// elements.add(getElement(podStack)); +// } +// } +// +// int silkTouch = ItemUtils.getEnchantmentLevel(Enchantments.SILK_TOUCH, accessor.getPlayer().getMainHandItem(), accessor.getPlayer().registryAccess()); +// int fortune = ItemUtils.getEnchantmentLevel(Enchantments.FORTUNE, accessor.getPlayer().getMainHandItem(), accessor.getPlayer().registryAccess()); +// if (lastVolume.getVolume() > 0) { +// LogsAndSticks las = species.getLogsAndSticks(lastVolume, silkTouch > 0, fortune); +// List logStacks = las.logs; +// if (!logStacks.isEmpty()) { +// for (ItemStack logStack : logStacks) { +// elements.add(getElement(logStack)); +// } +// } +// if (las.sticks > 0) { +// ItemStack stickStack = species.getFamily().getStickStack(las.sticks); +// if (!stickStack.isEmpty()) { +// elements.add(getElement(stickStack)); +// } +// } +// } +// +// tooltip.add(elements.removeFirst()); +// elements.forEach(tooltip::append); +// tooltip.add(ElementHelper.INSTANCE.spacer(0, 2)); } } @@ -167,13 +167,13 @@ private Species getWailaSpecies(Level level, BlockPos pos) { return TreeHelper.getBestGuessSpecies(level, pos); } - private static IElement getElement(ItemStack stack) { - if (!stack.isEmpty()) { - return ElementHelper.INSTANCE.item(stack); - } else { - return ElementHelper.INSTANCE.spacer(0, 0); - } - } +// private static IElement getElement(ItemStack stack) { +// if (!stack.isEmpty()) { +// return ElementHelper.INSTANCE.item(stack); +// } else { +// return ElementHelper.INSTANCE.spacer(0, 0); +// } +// } @Override public Identifier getUid() { diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/CommonEventHandler.java b/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/CommonEventHandler.java index 769c923d6..e1f4feb05 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/CommonEventHandler.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/CommonEventHandler.java @@ -15,7 +15,7 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLevelEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; @@ -32,16 +32,16 @@ public class CommonEventHandler { public static void RegisterEvents(){ - ServerTickEvents.START_WORLD_TICK.register((level)->{ + ServerTickEvents.START_LEVEL_TICK.register((level)->{ FutureBreak.process(level); - SeasonHelper.updateTick(level, level.getDayTime()); + SeasonHelper.updateTick(level, level.getDefaultClockTime()); }); - ServerWorldEvents.LOAD.register(((minecraftServer, serverLevel) -> { + ServerLevelEvents.LOAD.register(((minecraftServer, serverLevel) -> { BiomeDatabases.populateBlacklistFromConfig(); })); - ServerWorldEvents.UNLOAD.register(((minecraftServer, serverLevel) -> { + ServerLevelEvents.UNLOAD.register(((minecraftServer, serverLevel) -> { DynamicTreeFeature.DISC_PROVIDER.unloadWorld(serverLevel); })); @@ -57,13 +57,13 @@ public static void RegisterEvents(){ PlayerBlockBreakEvents.BEFORE.register((level, player, pos, state, blockEntity) -> { Block block = state.getBlock(); if (block instanceof BranchBlock branchBlock) { - return branchBlock.onDestroyedByPlayer(state, level, pos, player, true, level.getFluidState(pos)); + return branchBlock.onDestroyedByPlayer(state, level, pos, player, player.getMainHandItem(), true, level.getFluidState(pos)); } else if (block instanceof TrunkShellBlock trunkShellBlock) { - return trunkShellBlock.onDestroyedByPlayer(state, level, pos, player, true, level.getFluidState(pos)); + return trunkShellBlock.onDestroyedByPlayer(state, level, pos, player, player.getMainHandItem(), true, level.getFluidState(pos)); } else if (block instanceof SoilBlock soilBlock) { return soilBlock.onDestroyedByPlayer(state, level, pos, player, true, level.getFluidState(pos)); } else if (block instanceof PottedSaplingBlock pottedSaplingBlock) { - return pottedSaplingBlock.onDestroyedByPlayer(state, level, pos, player, true, level.getFluidState(pos)); + return pottedSaplingBlock.onDestroyedByPlayer(state, level, pos, player, player.getMainHandItem(), true, level.getFluidState(pos)); } return true; }); @@ -79,10 +79,9 @@ public FabricReloadListener() { } @Override - public CompletableFuture reload(PreparationBarrier stage, ResourceManager resourceManager, - ProfilerFiller preparationsProfiler, ProfilerFiller reloadProfiler, - Executor backgroundExecutor, Executor gameExecutor) { - return super.reload(stage, resourceManager, preparationsProfiler, reloadProfiler, backgroundExecutor, gameExecutor); + public CompletableFuture reload(SharedState sharedState, Executor backgroundExecutor, + PreparationBarrier preparationBarrier, Executor gameExecutor) { + return super.reload(sharedState, backgroundExecutor, preparationBarrier, gameExecutor); } @Override diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/VanillaSaplingEventHandler.java b/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/VanillaSaplingEventHandler.java index 52614ead8..05fd7eede 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/VanillaSaplingEventHandler.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/event/handler/VanillaSaplingEventHandler.java @@ -47,13 +47,13 @@ private static InteractionResult onUseBlock(Player player, Level level, Interact return InteractionResult.PASS; } - Species targetSpecies = DynamicSaplingBlock.SAPLING_REPLACERS.get(block); - Species species = targetSpecies.selfOrLocationOverride(level, placePos); - - if (species.overrideSaplingReplacementWhenCrouching() && player.isCrouching()){ + if (!DynamicSaplingBlock.shouldReplaceSaplingWhenPlaced(block.defaultBlockState())) { return InteractionResult.PASS; } + Species targetSpecies = DynamicSaplingBlock.SAPLING_REPLACERS.get(block); + Species species = targetSpecies.selfOrLocationOverride(level, placePos); + if (!species.plantSapling(level, placePos, targetSpecies != species)) { if (!player.isCreative()) stack.grow(1); return InteractionResult.SUCCESS; diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinVegetationBlock.java b/fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinVegetationBlock.java index 390acb646..d2cd47652 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinVegetationBlock.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinVegetationBlock.java @@ -24,7 +24,8 @@ private void canSurvive(BlockState state, LevelReader level, BlockPos pos, Callb BlockState belowBlockState = level.getBlockState(blockpos); if (belowBlockState.getBlock() instanceof BasicRootsBlock roots){ if (belowBlockState.getValue(BasicRootsBlock.LAYER) == BasicRootsBlock.Layer.COVERED){ - Block block = BasicRootsBlock.Layer.COVERED.getPrimitive(roots.getFamily()).orElse(null); + if (!(roots.getFamily() instanceof com.dtteam.dynamictrees.tree.family.AerialRootsFamily rootsFamily)) return; + Block block = BasicRootsBlock.Layer.COVERED.getPrimitive(rootsFamily).orElse(null); if (block == null) return; cir.setReturnValue(mayPlaceOn(block.defaultBlockState(), level, blockpos)); } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/BakedModelBlockPottedSapling.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/BakedModelBlockPottedSapling.java deleted file mode 100644 index 38e47de31..000000000 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/BakedModelBlockPottedSapling.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.dtteam.dynamictrees.model; - -import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.core.Direction; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.List; - -public class BakedModelBlockPottedSapling extends ForwardingBakedModel { - - public BakedModelBlockPottedSapling(BakedModel basePotModel) { - this.wrapped = basePotModel; - } - - @Override - public boolean isVanillaAdapter() { - return true; - } - - @Override - @NotNull - public List getQuads(BlockState state, Direction face, RandomSource random) { - return new ArrayList<>(wrapped.getQuads(state, face, random)); - } -} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/BranchBlockUnbakedModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/BranchBlockUnbakedModel.java deleted file mode 100644 index 5217b2522..000000000 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/BranchBlockUnbakedModel.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.dtteam.dynamictrees.model; - -import com.dtteam.dynamictrees.model.baked.BasicBranchBlockBakedModel; -import com.dtteam.dynamictrees.model.baked.ThickBranchBlockBakedModel; -import com.dtteam.dynamictrees.tree.family.Family; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.Identifier; -import net.minecraft.world.inventory.InventoryMenu; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Collections; -import java.util.function.Function; - -public class BranchBlockUnbakedModel implements UnbakedModel { - - protected final Identifier barkTextureLocation; - protected final Identifier ringsTextureLocation; - protected final Identifier familyName; - protected final boolean forceThickness; - - public BranchBlockUnbakedModel(Identifier barkTextureLocation, Identifier ringsTextureLocation, @Nullable Identifier familyName, boolean forceThickness) { - this.barkTextureLocation = barkTextureLocation; - this.ringsTextureLocation = ringsTextureLocation; - this.familyName = familyName; - this.forceThickness = forceThickness; - } - - @Override - public Collection getDependencies() { - return Collections.emptyList(); - } - - @Override - public void resolveParents(Function resolver) { - } - - @Override - public BakedModel bake(ModelBaker baker, Function spriteGetter, ModelState state) { - TextureAtlasSprite barkSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, barkTextureLocation)); - TextureAtlasSprite ringsSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, ringsTextureLocation)); - - Family family = familyName != null ? Family.REGISTRY.get(familyName) : null; - boolean useThickModel = forceThickness || (family != null && family.isThick()); - - if (useThickModel) { - Identifier thickRingsLocation = ringsTextureLocation.withSuffix("_thick"); - TextureAtlasSprite thickRingsSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, thickRingsLocation)); - return new ThickBranchBlockBakedModel(barkSprite, ringsSprite, thickRingsSprite); - } - - return new BasicBranchBlockBakedModel(barkSprite, ringsSprite); - } -} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/DTModelLoadingPlugin.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/DTModelLoadingPlugin.java index ec4c14306..0cd0499d9 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/DTModelLoadingPlugin.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/DTModelLoadingPlugin.java @@ -8,239 +8,211 @@ import com.dtteam.dynamictrees.model.baked.BasicRootsBlockBakedModel; import com.dtteam.dynamictrees.model.baked.SurfaceRootBlockBakedModel; import com.dtteam.dynamictrees.model.baked.ThickBranchBlockBakedModel; -import com.dtteam.dynamictrees.tree.family.Family; +import com.dtteam.dynamictrees.model.blockstate.AerialRootsSoilBlockStateModel; +import com.dtteam.dynamictrees.model.blockstate.PottedSaplingBlockStateModel; +import com.dtteam.dynamictrees.model.blockstate.UnbakedBranchModel; +import com.dtteam.dynamictrees.model.blockstate.UnbakedCreakingHeartModel; +import com.dtteam.dynamictrees.model.blockstate.UnbakedRootsMossModel; +import com.dtteam.dynamictrees.model.blockstate.UnbakedRootsModel; +import com.dtteam.dynamictrees.model.blockstate.UnbakedSurfaceRootModel; +import com.dtteam.dynamictrees.model.parts.BranchModelPart; import com.dtteam.dynamictrees.tree.family.AerialRootsFamily; +import com.dtteam.dynamictrees.tree.family.Family; +import com.dtteam.dynamictrees.utility.IdentifierUtils; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin; import net.fabricmc.fabric.api.client.model.loading.v1.ModelModifier; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelIdentifier; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; -import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; -import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; - +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Model loading hooks for DT's dynamic models on Fabric. + * + *

The primary path is codec-based: blockstate JSONs using DT's custom model types + * ({@code dynamictrees:branch}, {@code dynamictrees:roots}, ...) are deserialized through the + * codecs registered in {@link #registerModelTypes()}, exactly mirroring NeoForge's + * {@code RegisterBlockStateModels} registrations. Note that Fabric dispatches these on the + * {@code "fabric:type"} key (NeoForge uses {@code "type"}), so DT's generated blockstates carry + * both keys. + * + *

The secondary path is an after-bake fallback for dynamic blocks whose blockstate definitions + * are missing or unparseable (e.g. add-on tree packs still shipping pre-26.2 assets): any branch, + * surface root or underground roots block state that did not resolve to a DT model gets a + * procedurally built model based on its family's primitive log textures. + */ public class DTModelLoadingPlugin implements ModelLoadingPlugin { - public static final Identifier POTTED_SAPLING_MODEL = DynamicTrees.location("potted_sapling"); - private static final Map BRANCH_MODEL_CACHE = new HashMap<>(); - private static final Map ROOT_MODEL_CACHE = new HashMap<>(); - private static final Map UNDERGROUND_ROOTS_MODEL_CACHE = new HashMap<>(); - private static boolean modelsInitialized = false; + public static final Identifier BRANCH = DynamicTrees.location("branch"); + public static final Identifier SURFACE_ROOT = DynamicTrees.location("surface_root"); + public static final Identifier ROOTS = DynamicTrees.location("roots"); + public static final Identifier CREAKING_HEART = DynamicTrees.location("creaking_heart"); + public static final Identifier POTTED_DYNAMIC_SAPLING = DynamicTrees.location("potted_dynamic_sapling"); + public static final Identifier AERIAL_ROOTS_SOIL = DynamicTrees.location("aerial_roots_soil"); + public static final Identifier ROOTS_MOSS = DynamicTrees.location("roots_moss"); + + private static final Map FALLBACK_MODEL_CACHE = new ConcurrentHashMap<>(); + + /** + * Registers the custom blockstate model codecs. Must only be called once. + */ + public static void registerModelTypes() { + CustomUnbakedBlockStateModel.register(BRANCH, UnbakedBranchModel.CODEC); + CustomUnbakedBlockStateModel.register(ROOTS, UnbakedRootsModel.CODEC); + CustomUnbakedBlockStateModel.register(CREAKING_HEART, UnbakedCreakingHeartModel.CODEC); + CustomUnbakedBlockStateModel.register(SURFACE_ROOT, UnbakedSurfaceRootModel.CODEC); + CustomUnbakedBlockStateModel.register(POTTED_DYNAMIC_SAPLING, PottedSaplingBlockStateModel.Unbaked.CODEC); + CustomUnbakedBlockStateModel.register(AERIAL_ROOTS_SOIL, AerialRootsSoilBlockStateModel.Unbaked.CODEC); + CustomUnbakedBlockStateModel.register(ROOTS_MOSS, UnbakedRootsMossModel.CODEC); + } @Override - public void onInitializeModelLoader(Context pluginContext) { - modelsInitialized = false; - BRANCH_MODEL_CACHE.clear(); - ROOT_MODEL_CACHE.clear(); - UNDERGROUND_ROOTS_MODEL_CACHE.clear(); - pluginContext.modifyModelAfterBake().register(ModelModifier.WRAP_PHASE, this::modifyModelAfterBake); + public void initialize(Context pluginContext) { + FALLBACK_MODEL_CACHE.clear(); + pluginContext.modifyBlockModelAfterBake().register(ModelModifier.WRAP_PHASE, DTModelLoadingPlugin::modifyModelAfterBake); } - private void initBranchModels(Function spriteGetter) { - if (modelsInitialized) return; - modelsInitialized = true; + private static BlockStateModel modifyModelAfterBake(BlockStateModel model, ModelModifier.AfterBakeBlock.Context context) { + // Models already loaded through DT's codecs (or another DT path) are left untouched. + if (model instanceof FabricDynamicBlockStateModel || model instanceof PottedSaplingBlockStateModel) { + return model; + } - for (Family family : Family.REGISTRY.getAll()) { - if (!family.isValid()) continue; + BlockState state = context.state(); + Block block = state.getBlock(); - family.getPrimitiveLog().ifPresent(primitiveLog -> { - Identifier primitiveLogId = BuiltInRegistries.BLOCK.getKey(primitiveLog); - Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveLogId.getNamespace(), "block/" + primitiveLogId.getPath()); - Identifier ringsTexture = barkTexture.withSuffix("_top"); + if (block instanceof BasicRootsBlock rootsBlock) { + BlockStateModel rootsModel = getOrCreateRootsModel(rootsBlock, state, context.baker()); + return rootsModel != null ? rootsModel : model; + } - AtomicReference barkRef = new AtomicReference<>(barkTexture); - AtomicReference ringsRef = new AtomicReference<>(ringsTexture); + if (block instanceof SurfaceRootBlock surfaceRootBlock) { + BlockStateModel rootModel = getOrCreateSurfaceRootModel(surfaceRootBlock, context.baker()); + return rootModel != null ? rootModel : model; + } - family.getTexturePath(Family.BRANCH).ifPresent(barkRef::set); - family.getTexturePath(Family.BRANCH_TOP).ifPresent(ringsRef::set); + if (block instanceof BranchBlock branchBlock) { + BlockStateModel branchModel = getOrCreateBranchModel(branchBlock, context.baker()); + return branchModel != null ? branchModel : model; + } - boolean isThick = family.isThick(); + return model; + } - family.getBranch().ifPresent(branch -> { - Identifier blockId = BuiltInRegistries.BLOCK.getKey(branch); - BakedModel model = createBranchModel(barkRef.get(), ringsRef.get(), isThick, spriteGetter); - BRANCH_MODEL_CACHE.put(blockId, model); - }); - }); + /////////////////////////////////////////// + // BRANCHES + /////////////////////////////////////////// - family.getPrimitiveStrippedLog().ifPresent(strippedLog -> { - Identifier strippedLogId = BuiltInRegistries.BLOCK.getKey(strippedLog); - Identifier strippedBarkTexture = Identifier.fromNamespaceAndPath(strippedLogId.getNamespace(), "block/" + strippedLogId.getPath()); - Identifier strippedRingsTexture = strippedBarkTexture.withSuffix("_top"); + @Nullable + private static BlockStateModel getOrCreateBranchModel(BranchBlock branchBlock, ModelBaker baker) { + Family family = branchBlock.getFamily(); + if (family == null || !family.isValid()) return null; - AtomicReference barkRef = new AtomicReference<>(strippedBarkTexture); - AtomicReference ringsRef = new AtomicReference<>(strippedRingsTexture); + Identifier blockId = BuiltInRegistries.BLOCK.getKey(branchBlock); - family.getTexturePath(Family.STRIPPED_BRANCH).ifPresent(barkRef::set); - family.getTexturePath(Family.STRIPPED_BRANCH_TOP).ifPresent(ringsRef::set); + boolean stripped = family.getStrippedBranch().map(b -> b == branchBlock).orElse(false); + Optional primitiveLog = stripped ? family.getPrimitiveStrippedLog() : family.getPrimitiveLog(); + if (primitiveLog.isEmpty()) return null; - boolean isThick = family.isThick(); + Identifier primitiveLogId = BuiltInRegistries.BLOCK.getKey(primitiveLog.get()); + Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveLogId.getNamespace(), "block/" + primitiveLogId.getPath()); + Identifier ringsTexture = IdentifierUtils.suffix(barkTexture, "_top"); - family.getStrippedBranch().ifPresent(strippedBranch -> { - Identifier blockId = BuiltInRegistries.BLOCK.getKey(strippedBranch); - BakedModel model = createBranchModel(barkRef.get(), ringsRef.get(), isThick, spriteGetter); - BRANCH_MODEL_CACHE.put(blockId, model); - }); - }); + Identifier barkOverride = family.getTexturePath(stripped ? Family.STRIPPED_BRANCH : Family.BRANCH).orElse(barkTexture); + Identifier ringsOverride = family.getTexturePath(stripped ? Family.STRIPPED_BRANCH_TOP : Family.BRANCH_TOP).orElse(ringsTexture); - family.getSurfaceRoot().ifPresent(surfaceRoot -> { - family.getPrimitiveLog().ifPresent(primitiveLog -> { - Identifier primitiveLogId = BuiltInRegistries.BLOCK.getKey(primitiveLog); - Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveLogId.getNamespace(), "block/" + primitiveLogId.getPath()); - AtomicReference barkRef = new AtomicReference<>(barkTexture); - family.getTexturePath(Family.BRANCH).ifPresent(barkRef::set); + boolean isThick = family.isThick(); - Identifier blockId = BuiltInRegistries.BLOCK.getKey(surfaceRoot); - BakedModel model = createRootModel(barkRef.get(), spriteGetter); - ROOT_MODEL_CACHE.put(blockId, model); - }); - }); + return FALLBACK_MODEL_CACHE.computeIfAbsent(blockId, id -> createBranchModel(barkOverride, ringsOverride, isThick, baker)); + } - if (family instanceof AerialRootsFamily rootsFamily) { - rootsFamily.getRoots().ifPresent(roots -> { - Identifier blockId = BuiltInRegistries.BLOCK.getKey(roots); + private static BlockStateModel createBranchModel(Identifier barkTexture, Identifier ringsTexture, boolean isThick, ModelBaker baker) { + Material.Baked barkMat = bakeMaterial(baker, barkTexture); + Material.Baked ringsMat = bakeMaterial(baker, ringsTexture); - rootsFamily.getPrimitiveRoots().ifPresent(primitiveRoots -> { - Identifier primitiveRootsId = BuiltInRegistries.BLOCK.getKey(primitiveRoots); - Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveRootsId.getNamespace(), "block/" + primitiveRootsId.getPath() + "_side"); - Identifier ringsTexture = Identifier.fromNamespaceAndPath(primitiveRootsId.getNamespace(), "block/" + primitiveRootsId.getPath() + "_top"); + BasicBranchBlockBakedModel regular = BasicBranchBlockBakedModel.bakeBasic(baker, + new BranchModelPart.UnbakedCore(barkMat), + new BranchModelPart.UnbakedSleeve(barkMat), + new BranchModelPart.UnbakedCore(ringsMat), + null); - AtomicReference barkRef = new AtomicReference<>(barkTexture); - AtomicReference ringsRef = new AtomicReference<>(ringsTexture); + if (isThick) { + Identifier thickRingsTexture = IdentifierUtils.suffix(ringsTexture, "_thick"); + Material.Baked thickRingsMat = bakeMaterial(baker, thickRingsTexture); + return ThickBranchBlockBakedModel.bakeThick(baker, regular, + new BranchModelPart.UnbakedThickTrunk(barkMat, false), + new BranchModelPart.UnbakedThickTrunk(thickRingsMat, true)); + } - family.getTexturePath(Family.ROOTS_SIDE).ifPresent(barkRef::set); - family.getTexturePath(Family.ROOTS_TOP).ifPresent(ringsRef::set); + return regular; + } - BakedModel model = createRootsBlockModel(barkRef.get(), ringsRef.get(), spriteGetter); - UNDERGROUND_ROOTS_MODEL_CACHE.put(blockId.withSuffix("_exposed"), model); - }); + /////////////////////////////////////////// + // SURFACE ROOTS + /////////////////////////////////////////// - rootsFamily.getPrimitiveFilledRoots().ifPresent(primitiveFilledRoots -> { - Identifier primitiveFilledRootsId = BuiltInRegistries.BLOCK.getKey(primitiveFilledRoots); - Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveFilledRootsId.getNamespace(), "block/" + primitiveFilledRootsId.getPath() + "_side"); - Identifier ringsTexture = Identifier.fromNamespaceAndPath(primitiveFilledRootsId.getNamespace(), "block/" + primitiveFilledRootsId.getPath() + "_top"); + @Nullable + private static BlockStateModel getOrCreateSurfaceRootModel(SurfaceRootBlock surfaceRootBlock, ModelBaker baker) { + Family family = surfaceRootBlock.getFamily(); + if (family == null || !family.isValid()) return null; - BakedModel model = createRootsBlockModel(barkTexture, ringsTexture, spriteGetter); - UNDERGROUND_ROOTS_MODEL_CACHE.put(blockId.withSuffix("_filled"), model); - }); + Optional primitiveLog = family.getPrimitiveLog(); + if (primitiveLog.isEmpty()) return null; + Identifier blockId = BuiltInRegistries.BLOCK.getKey(surfaceRootBlock); + Identifier primitiveLogId = BuiltInRegistries.BLOCK.getKey(primitiveLog.get()); + Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveLogId.getNamespace(), "block/" + primitiveLogId.getPath()); + Identifier barkOverride = family.getTexturePath(Family.BRANCH).orElse(barkTexture); - }); - } - } + return FALLBACK_MODEL_CACHE.computeIfAbsent(blockId, id -> SurfaceRootBlockBakedModel.bake(baker, barkOverride)); } - private BakedModel createBranchModel(Identifier barkTexture, Identifier ringsTexture, boolean isThick, Function spriteGetter) { - TextureAtlasSprite barkSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, barkTexture)); - TextureAtlasSprite ringsSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, ringsTexture)); + /////////////////////////////////////////// + // UNDERGROUND (AERIAL) ROOTS + /////////////////////////////////////////// - if (isThick) { - Identifier thickRingsTexture = ringsTexture.withSuffix("_thick"); - TextureAtlasSprite thickRingsSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, thickRingsTexture)); - return new ThickBranchBlockBakedModel(barkSprite, ringsSprite, thickRingsSprite); - } + @Nullable + private static BlockStateModel getOrCreateRootsModel(BasicRootsBlock rootsBlock, BlockState state, ModelBaker baker) { + if (!(rootsBlock.getFamily() instanceof AerialRootsFamily rootsFamily) || !rootsFamily.isValid()) return null; + if (!state.hasProperty(BasicRootsBlock.LAYER)) return null; - return new BasicBranchBlockBakedModel(barkSprite, ringsSprite); - } + BasicRootsBlock.Layer layer = state.getValue(BasicRootsBlock.LAYER); + Identifier blockId = BuiltInRegistries.BLOCK.getKey(rootsBlock); - private BakedModel createRootModel(Identifier barkTexture, Function spriteGetter) { - TextureAtlasSprite barkSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, barkTexture)); - return new SurfaceRootBlockBakedModel(barkSprite); - } - - private BakedModel createRootsBlockModel(Identifier barkTexture, Identifier ringsTexture, Function spriteGetter) { - TextureAtlasSprite barkSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, barkTexture)); - TextureAtlasSprite ringsSprite = spriteGetter.apply(new Material(InventoryMenu.BLOCK_ATLAS, ringsTexture)); - return new BasicRootsBlockBakedModel(barkSprite, ringsSprite); - } - - private BakedModel createFallbackRootsModel(AerialRootsFamily family, String variant, Function spriteGetter) { - if (variant.contains("layer=exposed")) { - return family.getPrimitiveRoots().map(primitiveRoots -> { + return switch (layer) { + case EXPOSED -> rootsFamily.getPrimitiveRoots().map(primitiveRoots -> { Identifier primitiveRootsId = BuiltInRegistries.BLOCK.getKey(primitiveRoots); Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveRootsId.getNamespace(), "block/" + primitiveRootsId.getPath() + "_side"); Identifier ringsTexture = Identifier.fromNamespaceAndPath(primitiveRootsId.getNamespace(), "block/" + primitiveRootsId.getPath() + "_top"); - return createRootsBlockModel(barkTexture, ringsTexture, spriteGetter); + + Identifier barkOverride = rootsFamily.getTexturePath(Family.ROOTS_SIDE).orElse(barkTexture); + Identifier ringsOverride = rootsFamily.getTexturePath(Family.ROOTS_TOP).orElse(ringsTexture); + + return FALLBACK_MODEL_CACHE.computeIfAbsent(IdentifierUtils.suffix(blockId, "_exposed"), id -> + BasicRootsBlockBakedModel.bakeRoots(baker, bakeMaterial(baker, barkOverride), bakeMaterial(baker, ringsOverride), false)); }).orElse(null); - } else if (variant.contains("layer=filled")) { - return family.getPrimitiveFilledRoots().map(primitiveFilledRoots -> { + case FILLED -> rootsFamily.getPrimitiveFilledRoots().map(primitiveFilledRoots -> { Identifier primitiveFilledRootsId = BuiltInRegistries.BLOCK.getKey(primitiveFilledRoots); Identifier barkTexture = Identifier.fromNamespaceAndPath(primitiveFilledRootsId.getNamespace(), "block/" + primitiveFilledRootsId.getPath() + "_side"); Identifier ringsTexture = Identifier.fromNamespaceAndPath(primitiveFilledRootsId.getNamespace(), "block/" + primitiveFilledRootsId.getPath() + "_top"); - return createRootsBlockModel(barkTexture, ringsTexture, spriteGetter); + + return FALLBACK_MODEL_CACHE.computeIfAbsent(IdentifierUtils.suffix(blockId, "_filled"), id -> + BasicRootsBlockBakedModel.bakeRoots(baker, bakeMaterial(baker, barkTexture), bakeMaterial(baker, ringsTexture), true)); }).orElse(null); - } - return null; + case COVERED -> null; // Covered roots keep their regular (soil-like) model. + }; } - private BakedModel modifyModelAfterBake(BakedModel model, ModelModifier.AfterBake.Context context) { - ModelIdentifier modelId = context.topLevelId(); - if (modelId == null) return model; - - if (modelId.id().equals(POTTED_SAPLING_MODEL)) { - return new BakedModelBlockPottedSapling(model); - } - - Identifier blockId = modelId.id(); - Block block = BuiltInRegistries.BLOCK.get(blockId); - - if (block instanceof BasicRootsBlock rootsBlock) { - initBranchModels(context.textureGetter()); - - String variant = modelId.variant(); - Identifier cacheKey; - if (variant.contains("layer=filled")) { - cacheKey = blockId.withSuffix("_filled"); - } else if (variant.contains("layer=exposed")) { - cacheKey = blockId.withSuffix("_exposed"); - } else if (variant.contains("layer=covered")) { - return model; - } else { - return model; - } - - BakedModel rootsModel = UNDERGROUND_ROOTS_MODEL_CACHE.get(cacheKey); - if (rootsModel != null) { - return rootsModel; - } - - if (rootsBlock.getFamily() instanceof AerialRootsFamily undergroundFamily) { - BakedModel fallbackModel = createFallbackRootsModel(undergroundFamily, variant, context.textureGetter()); - if (fallbackModel != null) { - UNDERGROUND_ROOTS_MODEL_CACHE.put(cacheKey, fallbackModel); - return fallbackModel; - } - } - return model; - } - - if (block instanceof SurfaceRootBlock) { - initBranchModels(context.textureGetter()); - - BakedModel rootModel = ROOT_MODEL_CACHE.get(blockId); - if (rootModel != null) { - return rootModel; - } - return model; - } - - if (block instanceof BranchBlock) { - initBranchModels(context.textureGetter()); - - BakedModel branchModel = BRANCH_MODEL_CACHE.get(blockId); - if (branchModel != null) { - return branchModel; - } - } - - - return model; + private static Material.Baked bakeMaterial(ModelBaker baker, Identifier texture) { + return baker.materials().get(new Material(texture), texture::toDebugFileName); } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/FabricDynamicBlockStateModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/FabricDynamicBlockStateModel.java new file mode 100644 index 000000000..df75da7d5 --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/FabricDynamicBlockStateModel.java @@ -0,0 +1,43 @@ +package com.dtteam.dynamictrees.model; + +import net.fabricmc.fabric.api.client.renderer.v1.mesh.QuadEmitter; +import net.fabricmc.fabric.api.client.renderer.v1.model.FabricBlockStateModel; +import net.fabricmc.fabric.api.client.renderer.v1.model.FabricBlockStateModelPart; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +/** + * Fabric mirror of NeoForge's {@code DynamicBlockStateModel}: a {@link BlockStateModel} whose + * geometry depends on level context. Implementors provide the level-aware + * {@link #collectParts(BlockAndTintGetter, BlockPos, BlockState, RandomSource, List)}; quads are + * emitted through the Fabric Renderer API's {@link FabricBlockStateModel#emitQuads} path. + */ +public interface FabricDynamicBlockStateModel extends BlockStateModel, FabricBlockStateModel { + + void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts); + + @Override + default void collectParts(RandomSource random, List parts) { + // Geometry is dynamic; without level context there is nothing meaningful to collect. + } + + @Override + default void emitQuads(QuadEmitter emitter, BlockAndTintGetter level, BlockPos pos, BlockState state, + RandomSource random, Predicate<@Nullable Direction> cullTest) { + List parts = new ArrayList<>(); + collectParts(level, pos, state, random, parts); + for (BlockStateModelPart part : parts) { + ((FabricBlockStateModelPart) part).emitQuads(emitter, cullTest); + } + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/FallingTreeEntityModelFabric.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/FallingTreeEntityModelFabric.java index d1baafeb4..9f5f52a73 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/FallingTreeEntityModelFabric.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/FallingTreeEntityModelFabric.java @@ -1,181 +1,17 @@ package com.dtteam.dynamictrees.model; -import com.dtteam.dynamictrees.api.network.BranchDestructionData; -import com.dtteam.dynamictrees.block.branch.BranchBlock; -import com.dtteam.dynamictrees.block.soil.SoilBlock; -import com.dtteam.dynamictrees.compat.continuity.WrappedModelHandler; import com.dtteam.dynamictrees.entity.FallingTreeEntity; -import com.dtteam.dynamictrees.model.baked.BasicBranchBlockBakedModel; import com.dtteam.dynamictrees.model.entity.FallingTreeEntityModel; -import com.dtteam.dynamictrees.tree.TreeHelper; -import com.dtteam.dynamictrees.tree.species.Species; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.BlockRenderDispatcher; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.Vec3; -import org.apache.commons.lang3.tuple.Pair; - -import java.util.ArrayList; -import java.util.List; +/** + * Fabric falling tree entity model. Since the 26.2 port, quad generation is fully handled by the + * common {@link FallingTreeEntityModel} (via {@code QuadManipulator} and the + * {@code BlockStateModelWithConnectionData} interface implemented by DT's Fabric block state models), + * so no Fabric-specific behavior remains. + */ public class FallingTreeEntityModelFabric extends FallingTreeEntityModel { public FallingTreeEntityModelFabric(FallingTreeEntity entity) { super(entity); } - - @Override - public List generateTreeQuads(FallingTreeEntity entity) { - BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); - BranchDestructionData destructionData = entity.getDestroyData(); - Direction cutDir = destructionData.cutDir; - - ArrayList treeQuads = new ArrayList<>(); - - int[] connectionArray = new int[6]; - - if (destructionData.getNumBranches() > 0) { - BlockState exState = destructionData.getBranchBlockState(0); - BlockPos cutPos = destructionData.cutPos; - if (exState != null) { - Species species = destructionData.species; - RandomSource random = entity.getRandom(); - - boolean rootyBlockAdded = false; - if (destructionData.soilState != null) { - BlockState soilState = destructionData.soilState; - if (soilState != null) { - BlockState soilBlock = soilState.getBlock(); - BakedModel rootyModel = dispatcher.getBlockModel(soilState); - BlockPos cutOffset = destructionData.getRelativeCutPos(); - treeQuads.addAll(toTreeQuadData( - getQuadsWithOffset(rootyModel, soilState, new Vec3(cutOffset.getX(), cutOffset.getY() - 1, cutOffset.getZ()), random), - destructionData.species.getFamily().getRootColor(soilState, soilBlock.getColorFromBark()), - soilState)); - rootyBlockAdded = true; - } - } - - BakedModel branchModel = dispatcher.getBlockModel(exState); - destructionData.getConnections(0, connectionArray); - boolean bottomRingsAdded = false; - if (!rootyBlockAdded && connectionArray[cutDir.get3DDataValue()] > 0) { - BlockPos offsetPos = destructionData.getRelativeCutPos().relative(cutDir); - float offset = (8 - Math.min(((BranchBlock) exState.getBlock()).getRadius(exState), BranchBlock.MAX_RADIUS)) / 16f; - int coreRadius = ((BranchBlock) exState.getBlock()).getRadius(exState); - treeQuads.addAll(toTreeQuadData( - getBottomRingQuads(branchModel, new Vec3(offsetPos.getX(), offsetPos.getY(), offsetPos.getZ()).scale(offset), coreRadius, cutDir), - exState)); - bottomRingsAdded = true; - } - - for (int index = 0; index < destructionData.getNumBranches(); index++) { - Block previousBranch = exState.getBlock(); - exState = destructionData.getBranchBlockState(index); - if (!previousBranch.equals(exState.getBlock())) { - branchModel = dispatcher.getBlockModel(exState); - } - BlockPos relPos = destructionData.getBranchRelPos(index); - destructionData.getConnections(index, connectionArray); - int coreRadius = ((BranchBlock) exState.getBlock()).getRadius(exState); - Direction forceRingDir = (index == 0 && bottomRingsAdded) ? cutDir : null; - treeQuads.addAll(toTreeQuadData( - getBranchQuadsWithConnections(branchModel, exState, new Vec3(relPos.getX(), relPos.getY(), relPos.getZ()), random, connectionArray, coreRadius, forceRingDir), - exState)); - } - - for (Pair leafLoc : destructionData.getAllLeavesWithPos()) { - BlockState leafState = leafLoc.getValue(); - List bakedQuads = getQuadsWithOffset(dispatcher.getBlockModel(leafState), leafState, - new Vec3(leafLoc.getKey().getX(), leafLoc.getKey().getY(), leafLoc.getKey().getZ()), random); - - treeQuads.addAll(toTreeQuadData(bakedQuads, species.leafColorMultiplier(entity.level(), - cutPos.offset(leafLoc.getKey())), leafState)); - } - } - } - - return treeQuads; - } - - private List getBottomRingQuads(BakedModel model, Vec3 offset, int coreRadius, Direction cutDir) { - List allQuads = new ArrayList<>(); - - if (model instanceof BasicBranchBlockBakedModel branchModel) { - for (BakedQuad quad : branchModel.getRingQuads(coreRadius)) { - if (quad.getDirection() == cutDir) { - allQuads.add(quad); - } - } - } - - return offsetAllQuads(offset, allQuads); - } - - private List getBranchQuadsWithConnections(BakedModel model, BlockState state, Vec3 offset, RandomSource random, int[] connections, int coreRadius, Direction forceRingDir) { - List allQuads = new ArrayList<>(); - - BasicBranchBlockBakedModel branchModel = WrappedModelHandler.getInstance().unwrapBranchModel(model); - if (branchModel != null) { - int twigRadius = state.getBlock() instanceof BranchBlock branchBlock - ? branchBlock.getFamily().getPrimaryThickness() - : 1; - - branchModel.collectQuads(coreRadius, connections, twigRadius, forceRingDir) - .values().forEach(allQuads::addAll); - } else { - for (Direction direction : Direction.values()) { - allQuads.addAll(model.getQuads(state, direction, random)); - } - allQuads.addAll(model.getQuads(state, null, random)); - } - - return offsetAllQuads(offset, allQuads); - } - - private List getQuadsWithOffset(BakedModel model, BlockState state, Vec3 offset, RandomSource random) { - List allQuads = new ArrayList<>(); - - for (Direction direction : Direction.values()) { - allQuads.addAll(model.getQuads(state, direction, random)); - } - allQuads.addAll(model.getQuads(state, null, random)); - - return offsetAllQuads(offset, allQuads); - } - - private List offsetAllQuads(Vec3 offset, List allQuads) { - if (offset.x() != 0 || offset.y() != 0 || offset.z() != 0) { - List offsetQuads = new ArrayList<>(); - for (BakedQuad quad : allQuads) { - offsetQuads.add(offsetQuad(quad, offset)); - } - return offsetQuads; - } - - return allQuads; - } - - private BakedQuad offsetQuad(BakedQuad quad, Vec3 offset) { - int[] vertexData = quad.getVertices().clone(); - - for (int i = 0; i < 4; i++) { - int baseIndex = i * 8; - float x = Float.intBitsToFloat(vertexData[baseIndex]) + (float) offset.x(); - float y = Float.intBitsToFloat(vertexData[baseIndex + 1]) + (float) offset.y(); - float z = Float.intBitsToFloat(vertexData[baseIndex + 2]) + (float) offset.z(); - vertexData[baseIndex] = Float.floatToRawIntBits(x); - vertexData[baseIndex + 1] = Float.floatToRawIntBits(y); - vertexData[baseIndex + 2] = Float.floatToRawIntBits(z); - } - - return new BakedQuad(vertexData, quad.getTintIndex(), quad.getDirection(), quad.getSprite(), quad.isShade()); - } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicBranchBlockBakedModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicBranchBlockBakedModel.java index eeed78a92..08057a83e 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicBranchBlockBakedModel.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicBranchBlockBakedModel.java @@ -1,270 +1,168 @@ package com.dtteam.dynamictrees.model.baked; +import com.dtteam.dynamictrees.api.network.Connections; import com.dtteam.dynamictrees.block.branch.BranchBlock; -import com.dtteam.dynamictrees.block.branch.ThickBranchBlock; -import com.google.common.collect.Maps; -import net.fabricmc.fabric.api.renderer.v1.mesh.*; -import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; -import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; -import net.fabricmc.fabric.api.renderer.v1.RendererAccess; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import net.minecraft.client.renderer.block.model.BlockFaceUV; -import net.minecraft.client.renderer.block.model.FaceBakery; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.BlockModelRotation; +import com.dtteam.dynamictrees.model.BlockStateModelWithConnectionData; +import com.dtteam.dynamictrees.model.BranchMultiPartHolder; +import com.dtteam.dynamictrees.model.FabricDynamicBlockStateModel; +import com.dtteam.dynamictrees.model.ModelConnections; +import com.dtteam.dynamictrees.model.ModelHelper; +import com.dtteam.dynamictrees.model.parts.BranchModelPart; +import com.dtteam.dynamictrees.tree.TreeHelper; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.core.Direction.Axis; -import net.minecraft.core.Direction.AxisDirection; import net.minecraft.util.RandomSource; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; -import java.util.*; -import java.util.function.Supplier; - -@SuppressWarnings("unchecked") -public class BasicBranchBlockBakedModel implements BakedModel, FabricBakedModel { - - protected final TextureAtlasSprite barkTexture; - protected final TextureAtlasSprite ringsTexture; - - public final List[][] sleevesQuads = new List[6][8]; - public final List[][] coresQuads = new List[3][8]; - public final List[] ringsQuads = new List[8]; - - public BasicBranchBlockBakedModel(TextureAtlasSprite barkTexture, TextureAtlasSprite ringsTexture) { - this.barkTexture = barkTexture; - this.ringsTexture = ringsTexture; - initModels(); - } - - private void initModels() { - for (int i = 0; i < 8; i++) { - int radius = i + 1; - if (radius < 8) { - for (Direction dir : Direction.values()) { - sleevesQuads[dir.get3DDataValue()][i] = bakeSleeve(radius, dir, barkTexture); - } +import java.util.List; + +/** + * Dynamic branch block model for Fabric. Mirrors the NeoForge {@code BranchBlockStateModel}, + * emitting level-aware geometry (branch connections) at chunk-build time via the Fabric + * Renderer API. + */ +public class BasicBranchBlockBakedModel implements FabricDynamicBlockStateModel, BlockStateModelWithConnectionData { + + protected final BranchMultiPartHolder cores; + protected final BranchMultiPartHolder sleeves; + protected final BranchMultiPartHolder rings; + protected final BranchMultiPartHolder sleeveRings; + + public BasicBranchBlockBakedModel(BranchMultiPartHolder cores, BranchMultiPartHolder sleeves, + BranchMultiPartHolder rings, BranchMultiPartHolder sleeveRings) { + this.cores = cores; + this.sleeves = sleeves; + this.rings = rings; + this.sleeveRings = sleeveRings; + } + + /** + * Bakes a basic branch model out of the given unbaked parts. Mirrors the NeoForge + * {@code UnbakedBranchModel#bakeBasic}. + */ + public static BasicBranchBlockBakedModel bakeBasic( + ModelBaker baker, BranchModelPart.UnbakedCore unbakedCores, BranchModelPart.UnbakedSleeve unbakedSleeves, + BranchModelPart.UnbakedCore unbakedRings, @Nullable BranchModelPart.UnbakedSleeve unbakedSleeveRings) { + BranchMultiPartHolder sleeves = new BranchMultiPartHolder(); + BranchMultiPartHolder cores = new BranchMultiPartHolder(); + BranchMultiPartHolder rings = new BranchMultiPartHolder(); + BranchMultiPartHolder sleeveRings = new BranchMultiPartHolder(); + + for (int radius = 1; radius <= BranchBlock.MAX_RADIUS; radius++) { + if (radius < BranchBlock.MAX_RADIUS) { + sleeves.putAllParts(radius, unbakedSleeves.bakeAllSides(baker, radius)); } - coresQuads[0][i] = bakeCore(radius, Axis.Y, barkTexture); - coresQuads[1][i] = bakeCore(radius, Axis.Z, barkTexture); - coresQuads[2][i] = bakeCore(radius, Axis.X, barkTexture); - - ringsQuads[i] = bakeCore(radius, Axis.Y, ringsTexture); - } - } - - public BlockElement generateSleevePart(int radius, Direction dir) { - int dradius = radius * 2; - int halfSize = (16 - dradius) / 2; - int halfSizeX = dir.getStepX() != 0 ? halfSize : dradius; - int halfSizeY = dir.getStepY() != 0 ? halfSize : dradius; - int halfSizeZ = dir.getStepZ() != 0 ? halfSize : dradius; - int move = 16 - halfSize; - int centerX = 16 + (dir.getStepX() * move); - int centerY = 16 + (dir.getStepY() * move); - int centerZ = 16 + (dir.getStepZ() * move); - - Vector3f posFrom = new Vector3f((centerX - halfSizeX) / 2f, (centerY - halfSizeY) / 2f, (centerZ - halfSizeZ) / 2f); - Vector3f posTo = new Vector3f((centerX + halfSizeX) / 2f, (centerY + halfSizeY) / 2f, (centerZ + halfSizeZ) / 2f); - - boolean negative = dir.getAxisDirection() == AxisDirection.NEGATIVE; - if (dir.getAxis() == Axis.Z) { - negative = !negative; - } - - Map mapFacesIn = Maps.newEnumMap(Direction.class); - - for (Direction face : Direction.values()) { - if (dir.getOpposite() != face) { - BlockFaceUV uvface = null; - if (dir == face) { - if (radius == 1) { - uvface = new BlockFaceUV(new float[]{8 - radius, 8 - radius, 8 + radius, 8 + radius}, 0); - } - } else { - uvface = new BlockFaceUV(new float[]{8 - radius, negative ? 16 - halfSize : 0, 8 + radius, negative ? 16 : halfSize}, getFaceAngle(dir.getAxis(), face)); - } - if (uvface != null) { - mapFacesIn.put(face, new BlockElementFace(null, -1, null, uvface)); - } + if (unbakedSleeveRings != null) { + sleeveRings.putAllParts(radius, unbakedSleeveRings.bakeAllSides(baker, radius)); } - } - return new BlockElement(posFrom, posTo, mapFacesIn, null, true); - } + cores.putAllParts(Direction.Axis.Y, radius, unbakedCores.bakeAllSides(baker, radius, Direction.Axis.Y)); //DOWN<->UP + cores.putAllParts(Direction.Axis.Z, radius, unbakedCores.bakeAllSides(baker, radius, Direction.Axis.Z)); //NORTH<->SOUTH + cores.putAllParts(Direction.Axis.X, radius, unbakedCores.bakeAllSides(baker, radius, Direction.Axis.X)); //WEST<->EAST - public List bakeSleeve(int radius, Direction dir, TextureAtlasSprite bark) { - BlockElement part = generateSleevePart(radius, dir); - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - - for (Map.Entry e : part.faces.entrySet()) { - Direction face = e.getKey(); - quads.add(faceBakery.bakeQuad(part.from, part.to, e.getValue(), bark, face, BlockModelRotation.X0_Y0, part.rotation, true)); + rings.putAllParts(radius, unbakedRings.bakeAllSides(baker, radius, Direction.Axis.Y)); } - return quads; + return new BasicBranchBlockBakedModel(cores, sleeves, rings, sleeveRings); } - protected BlockElement generateCorePart(int radius, Axis axis) { - Vector3f posFrom = new Vector3f(8 - radius, 8 - radius, 8 - radius); - Vector3f posTo = new Vector3f(8 + radius, 8 + radius, 8 + radius); - - Map mapFacesIn = Maps.newEnumMap(Direction.class); - - for (Direction face : Direction.values()) { - BlockFaceUV uvface = new BlockFaceUV(new float[]{8 - radius, 8 - radius, 8 + radius, 8 + radius}, getFaceAngle(axis, face)); - mapFacesIn.put(face, new BlockElementFace(null, -1, null, uvface)); - } - - return new BlockElement(posFrom, posTo, mapFacesIn, null, true); - } - - public List bakeCore(int radius, Axis axis, TextureAtlasSprite icon) { - BlockElement part = generateCorePart(radius, axis); - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - - for (Map.Entry e : part.faces.entrySet()) { - Direction face = e.getKey(); - quads.add(faceBakery.bakeQuad(part.from, part.to, e.getValue(), icon, face, BlockModelRotation.X0_Y0, part.rotation, true)); - } - - return quads; - } - - public int getFaceAngle(Axis axis, Direction face) { - if (axis == Axis.Y) { - return 0; - } else if (axis == Axis.Z) { - return switch (face) { - case UP -> 0; - case WEST -> 270; - case DOWN -> 180; - default -> 90; - }; - } else { - return (face == Direction.NORTH) ? 270 : 90; - } - } + /////////////////////////////////////////// + // FABRIC DYNAMIC GEOMETRY + /////////////////////////////////////////// @Override - public boolean isVanillaAdapter() { - return false; + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts) { + collectParts(state, parts, ModelHelper.getModelConnections(level, pos, state)); } @Override - public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { - EnumMap> bakedQuads = collectQuads(blockView, state, pos); - if (bakedQuads == null) return; - - QuadEmitter emitter = context.getEmitter(); - RenderMaterial material = getRenderMaterial(); - if (material == null) return; - - bakedQuads.forEach((dir, quads) -> - emitQuads(dir, emitter, material, quads)); - } - - protected @Nullable EnumMap> collectQuads(BlockAndTintGetter getter, BlockState state, BlockPos pos) { - if (state == null) return null; - - final int coreRadius = getRadius(state); - if (coreRadius <= 0 || coreRadius > maxBranchRadius()) return null; - - int[] connections = new int[]{0, 0, 0, 0, 0, 0}; - int twigRadius = 1; - - if (state.getBlock() instanceof BranchBlock branchBlock) { - connections = branchBlock.getConnectionData(getter, pos, state).getAllRadii(); - twigRadius = branchBlock.getFamily().getPrimaryThickness(); - } - - return collectQuads(coreRadius, connections, twigRadius, null); + public Object createGeometryKey(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random) { + return ModelHelper.getModelConnections(level, pos, state); } - protected int maxBranchRadius() { - return ThickBranchBlock.MAX_RADIUS; - } + /////////////////////////////////////////// + // PART COLLECTION + /////////////////////////////////////////// - public EnumMap> collectQuads(int coreRadius, int[] connections, int twigRadius, Direction forceRingDir) { - int numConnections = 0; - for (int i : connections) { - numConnections += (i != 0) ? 1 : 0; + @Override + public void collectParts(BlockState state, List parts, Connections connectionsData) { + final int coreRadius = TreeHelper.getRadius(state); + if (coreRadius > 8 || coreRadius == 0) return; + if (!(connectionsData instanceof ModelConnections modelConnections)) return; + + final int[] connections = connectionsData.getAllRadii(); + final Direction forceRingDir = modelConnections.getRingOnly(); + final int twigRadius = modelConnections.getFamily().getPrimaryThickness(); + final int numConnections = countConnections(connections); + + final Direction sourceDir = get3DSourceDir(coreRadius, connections); + final Direction.Axis coreDir = sourceDir == null ? Direction.Axis.Y : sourceDir.getAxis(); + final Direction coreRingDir = (numConnections == 1 && sourceDir != null) ? sourceDir.getOpposite() : null; + + if (forceRings(numConnections, forceRingDir)) { + addPart(parts, rings.getPart(forceRingDir, coreRadius)); + } else { + for (Direction face : Direction.values()) { + gatherCoreParts(parts, face, coreRadius, connections, coreRingDir, coreDir); + gatherSleeveParts(parts, face, coreRadius, connections, twigRadius); + } } - - Direction sourceDir = getSourceDir(coreRadius, connections); - int coreDir = resolveCoreDir(sourceDir); - Direction coreRingDir = forceRingDir != null ? forceRingDir : - ((numConnections == 1 && sourceDir != null) ? sourceDir.getOpposite() : null); - - EnumMap> bakedQuads = new EnumMap<>(Direction.class); - - for (Direction face : Direction.values()) { - List quads = bakedQuads.computeIfAbsent(face, dir->new ArrayList<>()); - if (coreRadius != connections[face.get3DDataValue()]) { - if (coreRingDir == null || coreRingDir != face) { - quads.addAll(coresQuads[coreDir][coreRadius - 1]); - } else { - quads.addAll(ringsQuads[coreRadius - 1]); - } + //The null side is usually empty, but roots have the cross. + addPart(parts, cores.getPart(coreDir, null, coreRadius)); + } + + private void gatherSleeveParts(List parts, Direction face, int coreRadius, int[] connections, int twigRadius) { + // Get quads for sleeves models. + for (Direction connDir : Direction.values()) { + final int idx = connDir.get3DDataValue(); + final int connRadius = connections[idx]; + if (connRadius == 0) continue; + // If the connection side matches the quadpull side then cull the sleeve face. + // Don't cull radius-1 connections for leaves (which are partly transparent). + if (coreRadius < 8 && connRadius <= twigRadius || face != connDir) { + addPart(parts, sleeves.getPart(connDir, connRadius)); } - - if (coreRadius != 8) { - for (Direction connDir : Direction.values()) { - int idx = connDir.get3DDataValue(); - int connRadius = connections[idx]; - if (connRadius > 0 && connRadius < 8 && (connRadius <= twigRadius || face != connDir)) { - quads.addAll(sleevesQuads[idx][connRadius - 1]); - } - } + if (face == connDir && !sleeveRings.isEmpty()) { + addPart(parts, sleeveRings.getPart(connDir, connRadius)); } } - return bakedQuads; } - public List getRingQuads(int radius){ - return ringsQuads[radius - 1]; - } + private void gatherCoreParts(List parts, Direction face, int coreRadius, int[] connections, Direction coreRingDir, Direction.Axis coreDir) { + if (coreRadius == connections[face.get3DDataValue()]) return; - protected static RenderMaterial getRenderMaterial() { - var renderer = RendererAccess.INSTANCE.getRenderer(); - if (renderer == null) return null; - MaterialFinder finder = renderer.materialFinder(); -// finder.disableAo(0, true); -// finder.disableDiffuse(0, true); + if (coreRingDir != null && coreRingDir == face) { + addPart(parts, rings.getPart(face, coreRadius)); + } else { + addPart(parts, cores.getPart(coreDir, face, coreRadius)); + } + } - return finder.find(); + protected static void addPart(List parts, @Nullable BlockStateModelPart part) { + if (part == null) return; + parts.add(part); } - protected void emitQuads(Direction face, QuadEmitter emitter, RenderMaterial material, List quads) { - if (quads == null) return; - for (BakedQuad quad : quads) { - if (quad.getDirection() == face) { - emitter.fromVanilla(quad, material, face); - emitter.emit(); - } - } + private static boolean forceRings(int numConnections, Direction forceRingDir) { + return numConnections == 0 && forceRingDir != null; } - @Override - public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + public static int countConnections(int[] connections) { + int numConnections = 0; + for (int i : connections) { + numConnections += (i != 0) ? 1 : 0; + } + return numConnections; } @Nullable - protected Direction getSourceDir(int coreRadius, int[] connections) { + public static Direction get3DSourceDir(int coreRadius, int[] connections) { int largestConnection = 0; Direction sourceDir = null; @@ -277,59 +175,22 @@ protected Direction getSourceDir(int coreRadius, int[] connections) { } if (largestConnection < coreRadius) { - sourceDir = null; + sourceDir = null; //Has no source node } return sourceDir; } - protected int resolveCoreDir(@Nullable Direction dir) { - if (dir == null) { - return 0; - } - return dir.get3DDataValue() >> 1; - } - - protected int getRadius(BlockState blockState) { - return ((BranchBlock) blockState.getBlock()).getRadius(blockState); - } - - @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) { - return Collections.emptyList(); - } - - @Override - public boolean useAmbientOcclusion() { - return true; - } - - @Override - public boolean isGui3d() { - return false; - } - - @Override - public boolean usesBlockLight() { - return false; - } - - @Override - public boolean isCustomRenderer() { - return false; - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return barkTexture; - } + /////////////////////////////////////////// + // VANILLA MODEL METHODS + /////////////////////////////////////////// @Override - public ItemTransforms getTransforms() { - return ItemTransforms.NO_TRANSFORMS; + public Material.Baked particleMaterial() { + return cores.getFirstMaterial(); } @Override - public ItemOverrides getOverrides() { - return ItemOverrides.EMPTY; + public @BakedQuad.MaterialFlags int materialFlags() { + return cores.materialFlags(); } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicRootsBlockBakedModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicRootsBlockBakedModel.java index 37ab0a461..196a17ade 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicRootsBlockBakedModel.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicRootsBlockBakedModel.java @@ -1,132 +1,36 @@ package com.dtteam.dynamictrees.model.baked; -import com.dtteam.dynamictrees.block.branch.BasicRootsBlock; -import com.dtteam.dynamictrees.block.branch.BranchBlock; -import com.google.common.collect.Maps; -import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; -import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import net.minecraft.client.renderer.block.model.BlockFaceUV; -import net.minecraft.client.renderer.block.model.FaceBakery; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -@SuppressWarnings("unchecked") +import com.dtteam.dynamictrees.model.BranchMultiPartHolder; +import com.dtteam.dynamictrees.model.parts.BranchModelPart; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.sprite.Material; + +/** + * Dynamic underground roots model for Fabric. Baked out of root-specific branch parts; + * the part collection logic is shared with {@link BasicBranchBlockBakedModel}. + * Mirrors the NeoForge {@code UnbakedRootsModel} baking. + */ public class BasicRootsBlockBakedModel extends BasicBranchBlockBakedModel { - final static float Z_FIGHTING_OFFSET = 0.001f; - - private final List[][] sleeveEndFaces = new List[6][8]; - - public BasicRootsBlockBakedModel(TextureAtlasSprite barkTexture, TextureAtlasSprite ringsTexture) { - super(barkTexture, ringsTexture); - initRootsModels(); + public BasicRootsBlockBakedModel(BasicBranchBlockBakedModel baked) { + super(baked.cores, baked.sleeves, baked.rings, baked.sleeveRings); } - private void initRootsModels() { - for (int i = 0; i < 8; i++) { - int radius = i + 1; - for (Direction dir : Direction.values()) { - sleeveEndFaces[dir.get3DDataValue()][i] = bakeSleeveFace(radius, dir, ringsTexture); - } + public static BasicRootsBlockBakedModel bakeRoots(ModelBaker baker, Material.Baked barkMat, Material.Baked ringsMat, boolean opaque) { + BasicBranchBlockBakedModel model; + if (opaque) { + model = bakeBasic(baker, + new BranchModelPart.UnbakedCore(barkMat), + new BranchModelPart.UnbakedSleeve(barkMat), + new BranchModelPart.UnbakedCore(ringsMat), + new BranchModelPart.UnbakedRootSleeveEnds(ringsMat)); + } else { + model = bakeBasic(baker, + new BranchModelPart.UnbakedRootCore(barkMat, true), + new BranchModelPart.UnbakedRootSleeve(barkMat), + new BranchModelPart.UnbakedRootCore(ringsMat, false), + null); } - } - - public List bakeSleeveFace(int radius, Direction dir, TextureAtlasSprite rings) { - int dradius = radius * 2; - int halfSize = (16 - dradius) / 2; - float halfSizeX = dir.getStepX() != 0 ? halfSize + Z_FIGHTING_OFFSET : dradius; - float halfSizeY = dir.getStepY() != 0 ? halfSize + Z_FIGHTING_OFFSET : dradius; - float halfSizeZ = dir.getStepZ() != 0 ? halfSize + Z_FIGHTING_OFFSET : dradius; - int move = 16 - halfSize; - int centerX = 16 + (dir.getStepX() * move); - int centerY = 16 + (dir.getStepY() * move); - int centerZ = 16 + (dir.getStepZ() * move); - - Vector3f posFrom = new Vector3f((centerX - halfSizeX) / 2f, (centerY - halfSizeY) / 2f, (centerZ - halfSizeZ) / 2f); - Vector3f posTo = new Vector3f((centerX + halfSizeX) / 2f, (centerY + halfSizeY) / 2f, (centerZ + halfSizeZ) / 2f); - - Map mapFacesIn = Maps.newEnumMap(Direction.class); - BlockFaceUV uvface = new BlockFaceUV(new float[]{8 - radius, 8 - radius, 8 + radius, 8 + radius}, 0); - mapFacesIn.put(dir, new BlockElementFace(dir, -1, "", uvface)); - - BlockElement part = new BlockElement(posFrom, posTo, mapFacesIn, null, true); - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - - for (Map.Entry e : part.faces.entrySet()) { - Direction face = e.getKey(); - quads.add(faceBakery.bakeQuad(part.from, part.to, e.getValue(), rings, face, BlockModelRotation.X0_Y0, part.rotation, true)); - } - - return quads; - } - - @Override - protected int getRadius(BlockState blockState) { - if (blockState.getBlock() instanceof BasicRootsBlock) { - if (blockState.hasProperty(BasicRootsBlock.RADIUS)) { - return blockState.getValue(BasicRootsBlock.RADIUS); - } - } - return super.getRadius(blockState); - } - - @Override - public EnumMap> collectQuads(int coreRadius, int[] connections, int twigRadius, Direction forceRingDir) { - int numConnections = 0; - for (int i : connections) { - numConnections += (i != 0) ? 1 : 0; - } - - Direction sourceDir = getSourceDir(coreRadius, connections); - int coreDir = resolveCoreDir(sourceDir); - Direction coreRingDir = (numConnections == 1 && sourceDir != null) ? sourceDir.getOpposite() : null; - - EnumMap> bakedQuads = new EnumMap<>(Direction.class); - - for (Direction face : Direction.values()) { - List quads = bakedQuads.computeIfAbsent(face, dir->new ArrayList<>()); - int connectionOnFace = connections[face.get3DDataValue()]; - if (coreRadius != connectionOnFace) { - if (coreRingDir == null || coreRingDir != face) { - quads.addAll(coresQuads[coreDir][coreRadius - 1]); - } else { - quads.addAll(ringsQuads[coreRadius - 1]); - } - } - - if (coreRadius != 8) { - for (Direction connDir : Direction.values()) { - int idx = connDir.get3DDataValue(); - int connRadius = connections[idx]; - if (connRadius > 0 && connRadius < 8 && (connRadius <= twigRadius || face != connDir)) { - quads.addAll(sleevesQuads[idx][connRadius - 1]); - } - } - } - - if (connectionOnFace > 0 && connectionOnFace <= coreRadius) { - quads.addAll(sleeveEndFaces[face.get3DDataValue()][connectionOnFace - 1]); - } - } - - return bakedQuads; + return new BasicRootsBlockBakedModel(model); } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/SurfaceRootBlockBakedModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/SurfaceRootBlockBakedModel.java index e7ad85fbf..1659e2f86 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/SurfaceRootBlockBakedModel.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/SurfaceRootBlockBakedModel.java @@ -1,323 +1,158 @@ package com.dtteam.dynamictrees.model.baked; +import com.dtteam.dynamictrees.api.network.Connections; import com.dtteam.dynamictrees.api.network.RootConnections; import com.dtteam.dynamictrees.block.branch.SurfaceRootBlock; +import com.dtteam.dynamictrees.model.BlockStateModelWithConnectionData; +import com.dtteam.dynamictrees.model.FabricDynamicBlockStateModel; +import com.dtteam.dynamictrees.model.ModelHelper; +import com.dtteam.dynamictrees.model.parts.SurfaceRootModelPart; import com.dtteam.dynamictrees.utility.CoordUtils; -import com.google.common.collect.Maps; -import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; -import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; -import net.fabricmc.fabric.api.renderer.v1.RendererAccess; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import net.minecraft.client.renderer.block.model.BlockFaceUV; -import net.minecraft.client.renderer.block.model.FaceBakery; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; -import java.util.*; -import java.util.function.Supplier; +import java.util.List; -@SuppressWarnings("unchecked") -public class SurfaceRootBlockBakedModel implements BakedModel, FabricBakedModel { +/** + * Dynamic surface root model for Fabric. Mirrors the NeoForge {@code SurfaceRootBlockStateModel}. + */ +public class SurfaceRootBlockBakedModel implements FabricDynamicBlockStateModel, BlockStateModelWithConnectionData { - protected final TextureAtlasSprite barkTexture; + protected final SurfaceRootModelPart[][] cores; + protected final SurfaceRootModelPart[][] sleeves; + protected final SurfaceRootModelPart[][] verts; + protected final Material.Baked particleMaterial; - public final List[][] sleevesQuads = new List[4][7]; - public final List[][] coresQuads = new List[2][8]; - public final List[][] vertsQuads = new List[4][8]; - - public SurfaceRootBlockBakedModel(TextureAtlasSprite barkTexture) { - this.barkTexture = barkTexture; - initModels(); + public SurfaceRootBlockBakedModel(SurfaceRootModelPart[][] cores, SurfaceRootModelPart[][] sleeves, + SurfaceRootModelPart[][] verts, Material.Baked particleMaterial) { + this.cores = cores; + this.sleeves = sleeves; + this.verts = verts; + this.particleMaterial = particleMaterial; } - private void initModels() { + /** + * Bakes a surface root model for the given bark texture. Mirrors the NeoForge + * {@code SurfaceRootBlockStateModel.Unbaked#bake}. + */ + public static SurfaceRootBlockBakedModel bake(ModelBaker baker, Identifier barkTexture) { + SurfaceRootModelPart[][] sleeves = new SurfaceRootModelPart[4][7]; + SurfaceRootModelPart[][] cores = new SurfaceRootModelPart[2][8]; //8 Cores for 2 axis(X, Z) with the bark texture on all 6 sides rotated appropriately. + SurfaceRootModelPart[][] verts = new SurfaceRootModelPart[4][8]; + + Material.Baked barkMat = baker.materials().get(new Material(barkTexture, false), barkTexture::toDebugFileName); + + SurfaceRootModelPart.UnbakedCore unbakedCores = new SurfaceRootModelPart.UnbakedCore(barkMat); + SurfaceRootModelPart.UnbakedSleeve unbakedSleeves = new SurfaceRootModelPart.UnbakedSleeve(barkMat); + SurfaceRootModelPart.UnbakedVert unbakedVerts = new SurfaceRootModelPart.UnbakedVert(barkMat); + for (int r = 0; r < 8; r++) { int radius = r + 1; if (radius < 8) { for (Direction dir : CoordUtils.HORIZONTALS) { int horIndex = dir.get2DDataValue(); - sleevesQuads[horIndex][r] = bakeSleeve(radius, dir); - vertsQuads[horIndex][r] = bakeVert(radius, dir); - } - } - coresQuads[0][r] = bakeCore(radius, Direction.Axis.Z); - coresQuads[1][r] = bakeCore(radius, Direction.Axis.X); - } - } - - public int getRadialHeight(int radius) { - return radius * 2; - } - - public List bakeSleeve(int radius, Direction dir) { - int radialHeight = getRadialHeight(radius); - - int dradius = radius * 2; - int halfSize = (16 - dradius) / 2; - int halfSizeX = dir.getStepX() != 0 ? halfSize : dradius; - int halfSizeZ = dir.getStepZ() != 0 ? halfSize : dradius; - int move = 16 - halfSize; - int centerX = 16 + (dir.getStepX() * move); - int centerZ = 16 + (dir.getStepZ() * move); - - Vector3f posFrom = new Vector3f((centerX - halfSizeX) / 2f, 0, (centerZ - halfSizeZ) / 2f); - Vector3f posTo = new Vector3f((centerX + halfSizeX) / 2f, radialHeight, (centerZ + halfSizeZ) / 2f); - - boolean sleeveNegative = dir.getAxisDirection() == Direction.AxisDirection.NEGATIVE; - if (dir.getAxis() == Direction.Axis.Z) { - sleeveNegative = !sleeveNegative; - } - - Map mapFacesIn = Maps.newEnumMap(Direction.class); - - for (Direction face : Direction.values()) { - if (dir.getOpposite() != face) { - BlockFaceUV uvface; - if (face.getAxis().isHorizontal()) { - boolean facePositive = face.getAxisDirection() == Direction.AxisDirection.POSITIVE; - uvface = new BlockFaceUV(new float[]{facePositive ? 16 - radialHeight : 0, (sleeveNegative ? 16 - halfSize : 0), facePositive ? 16 : radialHeight, (sleeveNegative ? 16 : halfSize)}, getFaceAngle(dir.getAxis(), face)); - } else { - uvface = new BlockFaceUV(new float[]{8 - radius, sleeveNegative ? 16 - halfSize : 0, 8 + radius, sleeveNegative ? 16 : halfSize}, getFaceAngle(dir.getAxis(), face)); + sleeves[horIndex][r] = unbakedSleeves.bake(baker, radius, dir); + verts[horIndex][r] = unbakedVerts.bake(baker, radius, dir); } - mapFacesIn.put(face, new BlockElementFace(null, -1, null, uvface)); - } - } - - BlockElement part = new BlockElement(posFrom, posTo, mapFacesIn, null, true); - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - - for (Map.Entry e : part.faces.entrySet()) { - Direction face = e.getKey(); - quads.add(faceBakery.bakeQuad(part.from, part.to, e.getValue(), barkTexture, face, BlockModelRotation.X0_Y0, part.rotation, true)); - } - - return quads; - } - - private List bakeVert(int radius, Direction dir) { - int radialHeight = getRadialHeight(radius); - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - - AABB partBoundary = new AABB(8 - radius, radialHeight, 8 - radius, 8 + radius, 16 + radialHeight, 8 + radius) - .move(dir.getStepX() * 7, 0, dir.getStepZ() * 7); - - for (int i = 0; i < 2; i++) { - AABB pieceBoundary = partBoundary.intersect(new AABB(0, 0, 0, 16, 16, 16).move(0, 16 * i, 0)); - - for (Direction face : Direction.values()) { - Map mapFacesIn = Maps.newEnumMap(Direction.class); - - BlockFaceUV uvface = new BlockFaceUV(modUV(getUVs(pieceBoundary, face)), getFaceAngle(Direction.Axis.Y, face)); - mapFacesIn.put(face, new BlockElementFace(null, -1, null, uvface)); - - Vector3f[] limits = AABBLimits(pieceBoundary); - - BlockElement part = new BlockElement(limits[0], limits[1], mapFacesIn, null, true); - quads.add(faceBakery.bakeQuad(part.from, part.to, part.faces.get(face), barkTexture, face, BlockModelRotation.X0_Y0, part.rotation, true)); - } - } - - return quads; - } - - public List bakeCore(int radius, Direction.Axis axis) { - int radialHeight = getRadialHeight(radius); - - Vector3f posFrom = new Vector3f(8 - radius, 0, 8 - radius); - Vector3f posTo = new Vector3f(8 + radius, radialHeight, 8 + radius); - - Map mapFacesIn = Maps.newEnumMap(Direction.class); - - for (Direction face : Direction.values()) { - BlockFaceUV uvface; - if (face.getAxis().isHorizontal()) { - boolean positive = face.getAxisDirection() == Direction.AxisDirection.POSITIVE; - uvface = new BlockFaceUV(new float[]{positive ? 16 - radialHeight : 0, 8 - radius, positive ? 16 : radialHeight, 8 + radius}, getFaceAngle(axis, face)); - } else { - uvface = new BlockFaceUV(new float[]{8 - radius, 8 - radius, 8 + radius, 8 + radius}, getFaceAngle(axis, face)); } - - mapFacesIn.put(face, new BlockElementFace(null, -1, null, uvface)); - } - - BlockElement part = new BlockElement(posFrom, posTo, mapFacesIn, null, true); - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - - for (Map.Entry e : part.faces.entrySet()) { - Direction face = e.getKey(); - quads.add(faceBakery.bakeQuad(part.from, part.to, e.getValue(), barkTexture, face, BlockModelRotation.X0_Y0, part.rotation, true)); - } - - return quads; - } - - public int getFaceAngle(Direction.Axis axis, Direction face) { - if (axis == Direction.Axis.Y) { - return 0; - } else if (axis == Direction.Axis.Z) { - return switch (face) { - case UP -> 0; - case WEST, NORTH -> 270; - case DOWN -> 180; - default -> 90; - }; - } else { - return (face == Direction.NORTH) ? 270 : 90; + cores[0][r] = unbakedCores.bake(baker, radius, Direction.Axis.Z); //NORTH<->SOUTH + cores[1][r] = unbakedCores.bake(baker, radius, Direction.Axis.X); //WEST<->EAST } - } - public float[] getUVs(AABB box, Direction face) { - return switch (face) { - case UP -> new float[]{(float) box.minX, (float) box.minZ, (float) box.maxX, (float) box.maxZ}; - case NORTH -> new float[]{16f - (float) box.maxX, (float) box.minY, 16f - (float) box.minX, (float) box.maxY}; - case SOUTH -> new float[]{(float) box.minX, (float) box.minY, (float) box.maxX, (float) box.maxY}; - case WEST -> new float[]{(float) box.minZ, (float) box.minY, (float) box.maxZ, (float) box.maxY}; - case EAST -> new float[]{16f - (float) box.maxZ, (float) box.minY, 16f - (float) box.minZ, (float) box.maxY}; - default -> new float[]{(float) box.minX, 16f - (float) box.minZ, (float) box.maxX, 16f - (float) box.maxZ}; - }; + return new SurfaceRootBlockBakedModel(cores, sleeves, verts, barkMat); } - public float[] modUV(float[] uvs) { - uvs[0] = (int) uvs[0] & 0xf; - uvs[1] = (int) uvs[1] & 0xf; - uvs[2] = (((int) uvs[2] - 1) & 0xf) + 1; - uvs[3] = (((int) uvs[3] - 1) & 0xf) + 1; - return uvs; - } - - public Vector3f[] AABBLimits(AABB aabb) { - return new Vector3f[]{ - new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ), - new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ), - }; - } + /////////////////////////////////////////// + // FABRIC DYNAMIC GEOMETRY + /////////////////////////////////////////// @Override - public boolean isVanillaAdapter() { - return false; + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts) { + collectParts(state, parts, ModelHelper.getRootConnections(level, pos, state)); } @Override - public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { - EnumMap> bakedQuads = collectQuads(blockView, state, pos); - if (bakedQuads == null) return; - - QuadEmitter emitter = context.getEmitter(); - RenderMaterial material = getRenderMaterial(); - if (material == null) return; - - bakedQuads.forEach((dir, quads) -> - emitQuads(dir, emitter, material, quads)); + public Object createGeometryKey(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random) { + return ModelHelper.getRootConnections(level, pos, state); } - @Nullable - public EnumMap> collectQuads(BlockAndTintGetter getter, BlockState state, BlockPos pos) { - if (state == null) return null; + /////////////////////////////////////////// + // PART COLLECTION + /////////////////////////////////////////// - int coreRadius = getRadius(state); - if (coreRadius <= 0 || coreRadius > 8) return null; + @Override + public void collectParts(BlockState state, List parts, Connections connectionsData) { + int coreRadius = 0; + if (state.getBlock() instanceof SurfaceRootBlock root) { + coreRadius = root.getRadius(state); + } + if (coreRadius == 0) return; int[] connections = new int[]{0, 0, 0, 0}; RootConnections.ConnectionLevel[] connectionLevels = RootConnections.PLACEHOLDER_CONNECTION_LEVELS.clone(); - if (state.getBlock() instanceof SurfaceRootBlock surfaceRootBlock) { - RootConnections connectionData = surfaceRootBlock.getConnectionData(getter, pos); - connections = connectionData.getAllRadii(); - connectionLevels = connectionData.getConnectionLevels(); + if (connectionsData instanceof RootConnections rootConnections) { + connections = rootConnections.getAllRadii(); + connectionLevels = rootConnections.getConnectionLevels(); } for (int i = 0; i < connections.length; i++) { connections[i] = Mth.clamp(connections[i], 0, coreRadius); } - Direction sourceDir = getSourceDir(coreRadius, connections); + //The source direction is the biggest connection from one of the horizontal directions + Direction sourceDir = get2DSourceDir(coreRadius, connections); if (sourceDir == null) { sourceDir = Direction.DOWN; } int coreDir = resolveCoreDir(sourceDir); boolean isGrounded = state.getValue(SurfaceRootBlock.GROUNDED); + if (isGrounded) { + parts.add(cores[coreDir][coreRadius - 1]); + } - EnumMap> bakedQuads = new EnumMap<>(Direction.class); - - for (Direction face : Direction.values()) { - List quads = bakedQuads.computeIfAbsent(face, dir->new ArrayList<>()); - if (isGrounded) { - quads.addAll(coresQuads[coreDir][coreRadius - 1]); - } - - if (coreRadius != 8) { - for (Direction connDir : CoordUtils.HORIZONTALS) { - int idx = connDir.get2DDataValue(); - int connRadius = connections[idx]; - if (connRadius > 0) { - if (isGrounded && sleevesQuads[idx][connRadius - 1] != null) { - quads.addAll(sleevesQuads[idx][connRadius - 1]); - } - if (connectionLevels[idx] == RootConnections.ConnectionLevel.HIGH && vertsQuads[idx][connRadius - 1] != null) { - quads.addAll(vertsQuads[idx][connRadius - 1]); - } + //Get quads for sleeves models + if (coreRadius != 8) { //Special case for r!=8.. If it's a solid block so it has no sleeves + for (Direction connDir : CoordUtils.HORIZONTALS) { + int idx = connDir.get2DDataValue(); + int connRadius = connections[idx]; + if (connRadius > 0) { + if (isGrounded && sleeves[idx][connRadius - 1] != null) { + parts.add(sleeves[idx][connRadius - 1]); + } + if (connectionLevels[idx] == RootConnections.ConnectionLevel.HIGH && verts[idx][connRadius - 1] != null) { + parts.add(verts[idx][connRadius - 1]); } } } } - return bakedQuads; - } - - protected static void emitQuads(Direction face, QuadEmitter emitter, RenderMaterial material, List quads) { - if (quads == null) return; - for (BakedQuad quad : quads) { - if (quad.getDirection() == face) { - emitter.fromVanilla(quad, material, null); - emitter.emit(); - } - } - } - - protected static RenderMaterial getRenderMaterial() { - var renderer = RendererAccess.INSTANCE.getRenderer(); - if (renderer == null) return null; - MaterialFinder finder = renderer.materialFinder(); - -// finder.disableAo(0, true); -// finder.disableDiffuse(0, true); - - return finder.find(); } - @Override - public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + /** + * Converts direction DUNSWE to 2 axis numbers for Z,X + */ + protected int resolveCoreDir(Direction dir) { + return dir.getAxis() == Direction.Axis.X ? 1 : 0; } - protected Direction getSourceDir(int coreRadius, int[] connections) { + @Nullable + public static Direction get2DSourceDir(int coreRadius, int[] connections) { int largestConnection = 0; Direction sourceDir = null; for (Direction dir : CoordUtils.HORIZONTALS) { - int horIndex = dir.get2DDataValue(); - int connRadius = connections[horIndex]; + int connRadius = connections[dir.get2DDataValue()]; if (connRadius > largestConnection) { largestConnection = connRadius; sourceDir = dir; @@ -325,56 +160,22 @@ protected Direction getSourceDir(int coreRadius, int[] connections) { } if (largestConnection < coreRadius) { - sourceDir = null; + sourceDir = null; //Has no source node } return sourceDir; } - protected int resolveCoreDir(Direction dir) { - return dir.getAxis() == Direction.Axis.X ? 1 : 0; - } - - protected int getRadius(BlockState blockState) { - return ((SurfaceRootBlock) blockState.getBlock()).getRadius(blockState); - } - - @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) { - return Collections.emptyList(); - } - - @Override - public boolean useAmbientOcclusion() { - return true; - } - - @Override - public boolean isGui3d() { - return false; - } - - @Override - public boolean usesBlockLight() { - return false; - } - - @Override - public boolean isCustomRenderer() { - return true; - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return barkTexture; - } + /////////////////////////////////////////// + // VANILLA MODEL METHODS + /////////////////////////////////////////// @Override - public ItemTransforms getTransforms() { - return ItemTransforms.NO_TRANSFORMS; + public Material.Baked particleMaterial() { + return particleMaterial; } @Override - public ItemOverrides getOverrides() { - return ItemOverrides.EMPTY; + public @BakedQuad.MaterialFlags int materialFlags() { + return this.cores[0][0].materialFlags(); } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/ThickBranchBlockBakedModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/ThickBranchBlockBakedModel.java index 8616a2817..a53f60c29 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/ThickBranchBlockBakedModel.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/baked/ThickBranchBlockBakedModel.java @@ -1,217 +1,97 @@ package com.dtteam.dynamictrees.model.baked; +import com.dtteam.dynamictrees.api.network.Connections; import com.dtteam.dynamictrees.block.branch.BranchBlock; import com.dtteam.dynamictrees.block.branch.ThickBranchBlock; -import com.dtteam.dynamictrees.utility.CoordUtils; -import com.dtteam.dynamictrees.utility.CoordUtils.Surround; -import com.google.common.collect.Maps; -import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; -import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; -import net.fabricmc.fabric.api.renderer.v1.RendererAccess; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import net.minecraft.client.renderer.block.model.BlockFaceUV; -import net.minecraft.client.renderer.block.model.FaceBakery; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.core.BlockPos; +import com.dtteam.dynamictrees.model.BranchMultiPartHolder; +import com.dtteam.dynamictrees.model.ModelConnections; +import com.dtteam.dynamictrees.model.parts.BranchModelPart; +import com.dtteam.dynamictrees.tree.TreeHelper; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.core.Direction; -import net.minecraft.core.Direction.Axis; -import net.minecraft.core.Vec3i; import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import org.joml.Vector3f; -import java.util.ArrayList; -import java.util.EnumMap; +import java.util.EnumSet; import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -@SuppressWarnings("unchecked") +/** + * Dynamic thick branch (trunk) model for Fabric. Mirrors the NeoForge {@code ThickBranchBlockStateModel}. + */ public class ThickBranchBlockBakedModel extends BasicBranchBlockBakedModel { - private final TextureAtlasSprite thickRingsTexture; - private final List[] trunksBarkQuads = new List[16]; - private final List[] trunksTopBarkQuads = new List[16]; - private final List[] trunksTopRingsQuads = new List[16]; - public final List[] trunksBotRingsQuads = new List[16]; + private final BranchMultiPartHolder trunkBark; // The trunk will always feature bark on its sides. + private final BranchMultiPartHolder trunkRings; // The trunk will feature rings on its top and bottom. - public ThickBranchBlockBakedModel(TextureAtlasSprite barkTexture, TextureAtlasSprite ringsTexture, TextureAtlasSprite thickRingsTexture) { - super(barkTexture, ringsTexture); - this.thickRingsTexture = thickRingsTexture; - initThickModels(); + public ThickBranchBlockBakedModel(BasicBranchBlockBakedModel fallback, + BranchMultiPartHolder trunkBark, BranchMultiPartHolder trunkRings) { + super(fallback.cores, fallback.sleeves, fallback.rings, fallback.sleeveRings); + this.trunkBark = trunkBark; + this.trunkRings = trunkRings; } - private void initThickModels() { - for (int i = 0; i < ThickBranchBlock.MAX_RADIUS_THICK - ThickBranchBlock.MAX_RADIUS; i++) { - int radius = i + ThickBranchBlock.MAX_RADIUS + 1; - trunksBarkQuads[i] = bakeTrunkBark(radius, this.barkTexture, true); - trunksTopBarkQuads[i] = bakeTrunkBark(radius, this.barkTexture, false); - trunksTopRingsQuads[i] = bakeTrunkRings(radius, thickRingsTexture, Direction.UP); - trunksBotRingsQuads[i] = bakeTrunkRings(radius, thickRingsTexture, Direction.DOWN); - } - } - - public List bakeTrunkBark(int radius, TextureAtlasSprite bark, boolean side) { - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - AABB wholeVolume = new AABB(8 - radius, 0, 8 - radius, 8 + radius, 16, 8 + radius); - - final Direction[] run = side ? CoordUtils.HORIZONTALS : new Direction[]{Direction.UP, Direction.DOWN}; - ArrayList offsets = new ArrayList<>(); - - for (Surround dir : Surround.values()) { - offsets.add(dir.getOffset()); - } - offsets.add(new Vec3i(0, 0, 0)); - - for (Direction face : run) { - final Vec3i dirVector = face.getNormal(); - - for (Vec3i offset : offsets) { - if (face.getAxis() == Axis.Y || new Vec3(dirVector.getX(), dirVector.getY(), dirVector.getZ()).add(new Vec3(offset.getX(), offset.getY(), offset.getZ())).lengthSqr() > 2.25) { - Vec3 scaledOffset = new Vec3(offset.getX() * 16, offset.getY() * 16, offset.getZ() * 16); - AABB partBoundary = new AABB(0, 0, 0, 16, 16, 16).move(scaledOffset).intersect(wholeVolume); - - Vector3f[] limits = aabbLimits(partBoundary); - - Map mapFacesIn = Maps.newEnumMap(Direction.class); - - BlockFaceUV uvface = new BlockFaceUV(modUV(getUVs(partBoundary, face)), getFaceAngle(Axis.Y, face)); - mapFacesIn.put(face, new BlockElementFace(null, -1, null, uvface)); - - BlockElement part = new BlockElement(limits[0], limits[1], mapFacesIn, null, true); - quads.add(faceBakery.bakeQuad(part.from, part.to, part.faces.get(face), bark, face, BlockModelRotation.X0_Y0, part.rotation, true)); - } - } + /** + * Bakes a thick branch model out of the given unbaked trunk parts and a regular branch fallback. + * Mirrors the NeoForge {@code UnbakedBranchModel#bakeThick}. + */ + public static ThickBranchBlockBakedModel bakeThick( + ModelBaker baker, BasicBranchBlockBakedModel fallback, + BranchModelPart.UnbakedThickTrunk unbakedBark, BranchModelPart.UnbakedThickTrunk unbakedRings) { + BranchMultiPartHolder trunksBark = new BranchMultiPartHolder(); + BranchMultiPartHolder trunksRings = new BranchMultiPartHolder(); + + for (int radius = BranchBlock.MAX_RADIUS + 1; radius <= ThickBranchBlock.MAX_RADIUS_THICK; radius++) { + trunksBark.putAllParts(radius, unbakedBark.bakeAllSides(baker, radius)); + trunksRings.putAllParts(radius, unbakedRings.bakeSides(baker, radius, EnumSet.of(Direction.UP, Direction.DOWN))); } - return quads; + return new ThickBranchBlockBakedModel(fallback, trunksBark, trunksRings); } - public List bakeTrunkRings(int radius, TextureAtlasSprite ring, Direction face) { - List quads = new ArrayList<>(); - FaceBakery faceBakery = new FaceBakery(); - AABB wholeVolume = new AABB(8 - radius, 0, 8 - radius, 8 + radius, 16, 8 + radius); - int wholeVolumeWidth = 48; - - ArrayList offsets = new ArrayList<>(); - - for (Surround dir : Surround.values()) { - offsets.add(dir.getOffset()); + @Override + public void collectParts(BlockState state, List parts, Connections connectionsData) { + int coreRadius = TreeHelper.getRadius(state); + if (coreRadius <= BranchBlock.MAX_RADIUS) { + super.collectParts(state, parts, connectionsData); + return; } - offsets.add(new Vec3i(0, 0, 0)); + coreRadius = Mth.clamp(coreRadius, BranchBlock.MAX_RADIUS + 1, ThickBranchBlock.MAX_RADIUS_THICK); + if (!(connectionsData instanceof ModelConnections modelConnections)) return; - for (Vec3i offset : offsets) { - Vec3 scaledOffset = new Vec3(offset.getX() * 16, offset.getY() * 16, offset.getZ() * 16); - AABB partBoundary = new AABB(0, 0, 0, 16, 16, 16).move(scaledOffset).intersect(wholeVolume); + final int[] connections = connectionsData.getAllRadii(); + final Direction forceRingDir = modelConnections.getRingOnly(); + final int twigRadius = modelConnections.getFamily().getPrimaryThickness(); - Vector3f posFrom = new Vector3f((float) partBoundary.minX, (float) partBoundary.minY, (float) partBoundary.minZ); - Vector3f posTo = new Vector3f((float) partBoundary.maxX, (float) partBoundary.maxY, (float) partBoundary.maxZ); + int numConnections = countConnections(connections); - Map mapFacesIn = Maps.newEnumMap(Direction.class); - float[] uvs = getRingsUvs(face, partBoundary, wholeVolumeWidth); + if (numConnections == 0 && forceRingDir != null) return; - BlockFaceUV uvFace = new BlockFaceUV(uvs, getFaceAngle(Axis.Y, face)); - mapFacesIn.put(face, new BlockElementFace(null, -1, null, uvFace)); - - BlockElement part = new BlockElement(posFrom, posTo, mapFacesIn, null, true); - quads.add(faceBakery.bakeQuad(part.from, part.to, part.faces.get(face), ring, face, BlockModelRotation.X0_Y0, part.rotation, true)); + if (forceRingDir != null) { + connections[forceRingDir.get3DDataValue()] = 0; + addPart(parts, trunkRings.getPart(forceRingDir, coreRadius)); } - return quads; - } - - private static float[] getRingsUvs(Direction face, AABB partBoundary, int wholeVolumeWidth) { - float textureOffsetX = -16f; - float textureOffsetZ = -16f; + boolean branchesAround = areBranchesAround(connections); - float minX = ((float) ((partBoundary.minX - textureOffsetX) / wholeVolumeWidth)) * 16f; - float maxX = ((float) ((partBoundary.maxX - textureOffsetX) / wholeVolumeWidth)) * 16f; - float minZ = ((float) ((partBoundary.minZ - textureOffsetZ) / wholeVolumeWidth)) * 16f; - float maxZ = ((float) ((partBoundary.maxZ - textureOffsetZ) / wholeVolumeWidth)) * 16f; - - if (face == Direction.DOWN) { - minZ = ((float) ((partBoundary.maxZ - textureOffsetZ) / wholeVolumeWidth)) * 16f; - maxZ = ((float) ((partBoundary.minZ - textureOffsetZ) / wholeVolumeWidth)) * 16f; + for (Direction face : Direction.values()) { + gatherTrunkParts(parts, face, connections, twigRadius, branchesAround, coreRadius); } - - return new float[]{minX, minZ, maxX, maxZ}; - } - - public static float[] getUVs(AABB box, Direction face) { - return switch (face) { - case UP -> new float[]{(float) box.minX, (float) box.minZ, (float) box.maxX, (float) box.maxZ}; - case NORTH -> new float[]{16f - (float) box.maxX, (float) box.minY, 16f - (float) box.minX, (float) box.maxY}; - case SOUTH -> new float[]{(float) box.minX, (float) box.minY, (float) box.maxX, (float) box.maxY}; - case WEST -> new float[]{(float) box.minZ, (float) box.minY, (float) box.maxZ, (float) box.maxY}; - case EAST -> new float[]{16f - (float) box.maxZ, (float) box.minY, 16f - (float) box.minZ, (float) box.maxY}; - default -> new float[]{(float) box.minX, 16f - (float) box.minZ, (float) box.maxX, 16f - (float) box.maxZ}; - }; } - public static float[] modUV(float[] uvs) { - uvs[0] = (int) uvs[0] & 0xf; - uvs[1] = (int) uvs[1] & 0xf; - uvs[2] = (((int) uvs[2] - 1) & 0xf) + 1; - uvs[3] = (((int) uvs[3] - 1) & 0xf) + 1; - return uvs; + private static boolean areBranchesAround(int[] connections) { + return connections[2] + connections[3] + connections[4] + connections[5] != 0; } - public static Vector3f[] aabbLimits(AABB aabb) { - return new Vector3f[]{ - new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ), - new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ), - }; - } - - @Override - protected int maxBranchRadius() { - return ThickBranchBlock.MAX_RADIUS_THICK; - } - - @Override - public EnumMap> collectQuads(int coreRadius, int[] connections, int twigRadius, Direction forceRingDir) { - if (coreRadius <= BranchBlock.MAX_RADIUS) { - return super.collectQuads(coreRadius, connections, twigRadius, forceRingDir); - } - - boolean branchesAround = connections[2] + connections[3] + connections[4] + connections[5] != 0; - - int radiusIndex = coreRadius - BranchBlock.MAX_RADIUS - 1; - if (radiusIndex >= trunksBarkQuads.length) return null; - - EnumMap> bakedQuads = new EnumMap<>(Direction.class); - - for (Direction face : Direction.values()) { - List quads = bakedQuads.computeIfAbsent(face, dir->new ArrayList<>()); - quads.addAll(trunksBarkQuads[radiusIndex]); - - if (face == Direction.UP || face == Direction.DOWN) { - if (connections[face.get3DDataValue()] < twigRadius && !branchesAround) { - quads.addAll(trunksTopRingsQuads[radiusIndex]); - } else if (connections[face.get3DDataValue()] < coreRadius) { - quads.addAll(trunksTopBarkQuads[radiusIndex]); - } + private void gatherTrunkParts(List parts, Direction face, int[] connections, int twigRadius, boolean branchesAround, int coreRadius) { + if (face == Direction.UP || face == Direction.DOWN) { + if (connections[face.get3DDataValue()] < twigRadius && !branchesAround) { + addPart(parts, this.trunkRings.getPart(face, coreRadius)); + } else if (connections[face.get3DDataValue()] < coreRadius) { + addPart(parts, this.trunkBark.getPart(face, coreRadius)); } + } else { + addPart(parts, trunkBark.getPart(face, coreRadius)); } - - return bakedQuads; - } - - @Override - public List getRingQuads(int radius) { - if (radius <= BranchBlock.MAX_RADIUS){ - return super.getRingQuads(radius); - } - return trunksBotRingsQuads[radius - BranchBlock.MAX_RADIUS - 1]; } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/AerialRootsSoilBlockStateModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/AerialRootsSoilBlockStateModel.java new file mode 100644 index 000000000..fe8493ca4 --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/AerialRootsSoilBlockStateModel.java @@ -0,0 +1,117 @@ +package com.dtteam.dynamictrees.model.blockstate; + +import com.dtteam.dynamictrees.model.BlockStateModelWithRadius; +import com.dtteam.dynamictrees.model.FabricDynamicBlockStateModel; +import com.dtteam.dynamictrees.model.parts.AerialRootSoilModelPart; +import com.dtteam.dynamictrees.tree.TreeHelper; +import com.dtteam.dynamictrees.tree.family.Family; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ResolvableModel; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.Identifier; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.List; +import java.util.Optional; + +/** + * Fabric port of the NeoForge {@code AerialRootsSoilBlockStateModel}; deserialized from + * blockstate JSONs with type {@code dynamictrees:aerial_roots_soil}. + */ +public record AerialRootsSoilBlockStateModel( + AerialRootSoilModelPart[] soilParts +) implements FabricDynamicBlockStateModel, BlockStateModelWithRadius { + + private record RadiusGeometryKey(AerialRootsSoilBlockStateModel model, int radius) {} + + @Override + public Object createGeometryKey(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random) { + return new RadiusGeometryKey(this, TreeHelper.getRadius(state)); + } + + @Override + public void collectParts(BlockState state, List parts, int radius) { + if (radius == 0 || radius > 8) return; + parts.add(soilParts[radius - 1]); + } + + @Override + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts) { + collectParts(state, parts, TreeHelper.getRadius(state)); + } + + public record Unbaked(Identifier end, Identifier overlay, Identifier overlay_end, Identifier side, Optional family) implements CustomUnbakedBlockStateModel { + + public static final String END_TEXTURE = "end"; + public static final String OVERLAY_TEXTURE = "overlay"; + public static final String OVERLAY_END_TEXTURE = "overlay_end"; + public static final String SIDE_TEXTURE = "side"; + public static final String TEXTURES = "textures"; + public static final String FAMILY = "family"; + + private record RootsSoilTextures(Identifier end, Identifier overlay, Identifier overlay_end, Identifier side) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Identifier.CODEC.fieldOf(END_TEXTURE).forGetter(RootsSoilTextures::end), + Identifier.CODEC.fieldOf(OVERLAY_TEXTURE).forGetter(RootsSoilTextures::overlay), + Identifier.CODEC.fieldOf(OVERLAY_END_TEXTURE).forGetter(RootsSoilTextures::overlay_end), + Identifier.CODEC.fieldOf(SIDE_TEXTURE).forGetter(RootsSoilTextures::side) + ).apply(i, RootsSoilTextures::new)); + } + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + RootsSoilTextures.CODEC.codec().fieldOf(TEXTURES).forGetter(m -> + new RootsSoilTextures(m.end(), m.overlay(), m.overlay_end(), m.side())), + Family.CODEC.optionalFieldOf(FAMILY).forGetter(Unbaked::family) + ).apply(i, (textures, family) -> + new Unbaked(textures.end(), textures.overlay(), textures.overlay_end(), textures.side(), family))); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public BlockStateModel bake(ModelBaker baker) { + AerialRootSoilModelPart[] soilParts = new AerialRootSoilModelPart[8]; + + Material.Baked endMat = bakeMaterial(baker, end); + Material.Baked overlayMat = bakeMaterial(baker, overlay); + Material.Baked overlayEndMat = bakeMaterial(baker, overlay_end); + Material.Baked sideMat = bakeMaterial(baker, side); + + AerialRootSoilModelPart.UnbakedPart unbakedPart = new AerialRootSoilModelPart.UnbakedPart(endMat, overlayMat, overlayEndMat, sideMat); + + for (int i = 0; i < 8; i++) { + soilParts[i] = unbakedPart.bake(baker, i + 1); + } + + return new AerialRootsSoilBlockStateModel(soilParts); + } + + private Material.Baked bakeMaterial(ModelBaker baker, Identifier texture) { + return baker.materials().get(new Material(texture, false), texture::toDebugFileName); + } + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) {} + } + + @Override + public @BakedQuad.MaterialFlags int materialFlags() { + return soilParts[0].materialFlags(); + } + + @Override + public Material.Baked particleMaterial() { + return soilParts[0].particleMaterial(); + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/PottedSaplingBlockStateModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/PottedSaplingBlockStateModel.java new file mode 100644 index 000000000..4b9680855 --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/PottedSaplingBlockStateModel.java @@ -0,0 +1,138 @@ +package com.dtteam.dynamictrees.model.blockstate; + +import com.dtteam.dynamictrees.block.sapling.PottedSaplingBlockEntity; +import com.dtteam.dynamictrees.model.FabricDynamicBlockStateModel; +import com.dtteam.dynamictrees.model.ModelHelper; +import com.dtteam.dynamictrees.tree.species.Species; +import com.mojang.math.Transformation; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.renderer.block.dispatch.ModelState; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ResolvableModel; +import net.minecraft.client.resources.model.ResolvedModel; +import net.minecraft.client.resources.model.SimpleModelWrapper; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.client.resources.model.sprite.TextureSlots; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.Identifier; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Fabric port of the NeoForge {@code PottedSaplingBlockStateModel}; deserialized from blockstate + * JSONs with type {@code dynamictrees:potted_dynamic_sapling}. Retrieves the potted species from + * the block entity (NeoForge uses model data instead). + */ +public record PottedSaplingBlockStateModel( + BlockStateModelPart pot, + Map saplings, + Material.Baked particleMaterial +) implements FabricDynamicBlockStateModel { + + @Override + public @BakedQuad.MaterialFlags int materialFlags() { + return this.pot.materialFlags(); + } + + @Nullable + private static Species getSpecies(BlockAndTintGetter level, BlockPos pos) { + return level.getBlockEntity(pos) instanceof PottedSaplingBlockEntity pottedSapling + ? pottedSapling.getSpecies() : null; + } + + @Override + public Object createGeometryKey(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random) { + return getSpecies(level, pos); + } + + @Override + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts) { + Species species = getSpecies(level, pos); + if (species == null || !species.isValid() || species.getSapling().isEmpty()) return; + + SimpleModelWrapper sapling = saplings.get(species); + if (sapling == null) return; + + parts.add(pot); + parts.add(sapling); + } + + public record Unbaked(Identifier modelLocation) implements CustomUnbakedBlockStateModel { + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Identifier.CODEC.fieldOf("model").forGetter(Unbaked::modelLocation) + ).apply(i, Unbaked::new)); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public BlockStateModel bake(ModelBaker modelBaker) { + ResolvedModel pot = modelBaker.getModel(modelLocation); + TextureSlots potSlots = pot.getTopTextureSlots(); + Material.Baked material = pot.resolveParticleMaterial(potSlots, modelBaker); + + SimpleModelWrapper bakedPot = new SimpleModelWrapper( + pot.bakeTopGeometry(potSlots, modelBaker, ModelHelper.noState()), + pot.getTopAmbientOcclusion(), + material + ); + + Map saplings = new HashMap<>(); + + for (Species species : Species.REGISTRY) { + if (species.getSapling().isPresent()) { + Identifier saplingModelLocation = species.getSaplingModelLocation(); + ResolvedModel resolved = modelBaker.getModel(saplingModelLocation); + + TextureSlots saplingSlots = resolved.getTopTextureSlots(); + saplings.put(species, new SimpleModelWrapper( + resolved.bakeTopGeometry(saplingSlots, modelBaker, OFFSET_UP), + resolved.getTopAmbientOcclusion(), + resolved.resolveParticleMaterial(saplingSlots, modelBaker))); + } + } + + return new PottedSaplingBlockStateModel(bakedPot, saplings, material); + } + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) { + resolver.markDependency(this.modelLocation); + // Also mark the sapling models so bake() can safely resolve them. + for (Species species : Species.REGISTRY) { + if (species.getSapling().isPresent()) { + resolver.markDependency(species.getSaplingModelLocation()); + } + } + } + } + + public static final ModelState OFFSET_UP = new ModelState() { + private static final Transformation TRANSFORM = new Transformation( + new Vector3f(0f, 0.25f, 0f), + null, null, null + ); + + @Override + @NotNull + public Transformation transformation() { + return TRANSFORM; + } + }; +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedBranchModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedBranchModel.java new file mode 100644 index 000000000..d17f9a023 --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedBranchModel.java @@ -0,0 +1,79 @@ +package com.dtteam.dynamictrees.model.blockstate; + +import com.dtteam.dynamictrees.DynamicTrees; +import com.dtteam.dynamictrees.model.baked.BasicBranchBlockBakedModel; +import com.dtteam.dynamictrees.model.baked.ThickBranchBlockBakedModel; +import com.dtteam.dynamictrees.model.parts.BranchModelPart; +import com.dtteam.dynamictrees.tree.family.Family; +import com.dtteam.dynamictrees.utility.IdentifierUtils; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ResolvableModel; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * Fabric port of the NeoForge {@code UnbakedBranchModel}; deserialized from blockstate JSONs + * with type {@code dynamictrees:branch}. + */ +public record UnbakedBranchModel(Identifier barkTexture, Identifier ringsTexture, Optional family) implements CustomUnbakedBlockStateModel { + + public static final String BARK_TEXTURE = "bark"; + public static final String RINGS_TEXTURE = "rings"; + public static final String TEXTURES = "textures"; + public static final String FAMILY = "family"; + + private record BranchTextures(Identifier bark, Identifier rings) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Identifier.CODEC.fieldOf(BARK_TEXTURE).forGetter(BranchTextures::bark), + Identifier.CODEC.fieldOf(RINGS_TEXTURE).forGetter(BranchTextures::rings) + ).apply(i, BranchTextures::new)); + } + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + BranchTextures.CODEC.codec().fieldOf(TEXTURES).forGetter(m -> new BranchTextures(m.barkTexture(), m.ringsTexture())), + Family.CODEC.optionalFieldOf(FAMILY).forGetter(UnbakedBranchModel::family) + ).apply(i, (textures, family) -> new UnbakedBranchModel(textures.bark(), textures.rings(), family))); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) {} + + @Override + public BlockStateModel bake(ModelBaker baker) { + Material.Baked barkMat = baker.materials().get(new Material(barkTexture), barkTexture::toDebugFileName); + Material.Baked ringsMat = baker.materials().get(new Material(ringsTexture), ringsTexture::toDebugFileName); + + BasicBranchBlockBakedModel regular = BasicBranchBlockBakedModel.bakeBasic(baker, + new BranchModelPart.UnbakedCore(barkMat), + new BranchModelPart.UnbakedSleeve(barkMat), + new BranchModelPart.UnbakedCore(ringsMat), + null); + + if (family.isPresent() && family.get().isThick()) { + Identifier thickRings = getThickRingsTexture(ringsTexture); + Material.Baked thickRingsMat = baker.materials().get(new Material(thickRings), thickRings::toDebugFileName); + + return ThickBranchBlockBakedModel.bakeThick(baker, regular, + new BranchModelPart.UnbakedThickTrunk(barkMat, false), + new BranchModelPart.UnbakedThickTrunk(thickRingsMat, true)); + } + return regular; + } + + private @NotNull Identifier getThickRingsTexture(Identifier ringsTexture) { + if (ringsTexture.equals(DynamicTrees.location("block/air"))) + return ringsTexture; + return IdentifierUtils.suffix(ringsTexture, "_thick"); + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedCreakingHeartModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedCreakingHeartModel.java new file mode 100644 index 000000000..6dea5956d --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedCreakingHeartModel.java @@ -0,0 +1,62 @@ +package com.dtteam.dynamictrees.model.blockstate; + +import com.dtteam.dynamictrees.model.baked.BasicBranchBlockBakedModel; +import com.dtteam.dynamictrees.model.parts.BranchModelPart; +import com.dtteam.dynamictrees.tree.family.Family; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ResolvableModel; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.resources.Identifier; + +import java.util.Optional; + +/** + * Fabric port of the NeoForge {@code UnbakedCreakingHeartModel}; deserialized from blockstate + * JSONs with type {@code dynamictrees:creaking_heart}. + */ +public record UnbakedCreakingHeartModel(Identifier heartBark, Identifier heartRings, Identifier bark, Optional family) implements CustomUnbakedBlockStateModel { + + public static final String BARK_TEXTURE = "bark"; + public static final String HEART_BARK_TEXTURE = "heart_bark"; + public static final String HEART_RINGS_TEXTURE = "heart_rings"; + public static final String TEXTURES = "textures"; + public static final String FAMILY = "family"; + + private record HeartTextures(Identifier bark, Identifier heartBark, Identifier heartRings) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Identifier.CODEC.fieldOf(BARK_TEXTURE).forGetter(HeartTextures::bark), + Identifier.CODEC.fieldOf(HEART_BARK_TEXTURE).forGetter(HeartTextures::heartBark), + Identifier.CODEC.fieldOf(HEART_RINGS_TEXTURE).forGetter(HeartTextures::heartRings) + ).apply(i, HeartTextures::new)); + } + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + HeartTextures.CODEC.codec().fieldOf(TEXTURES).forGetter(m -> new HeartTextures(m.bark, m.heartBark, m.heartRings)), + Family.CODEC.optionalFieldOf(FAMILY).forGetter(UnbakedCreakingHeartModel::family) + ).apply(i, (textures, family) -> new UnbakedCreakingHeartModel(textures.heartBark, textures.heartRings, textures.bark, family))); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) {} + + @Override + public BlockStateModel bake(ModelBaker baker) { + Material.Baked heartMat = baker.materials().get(new Material(heartBark), heartBark::toDebugFileName); + Material.Baked ringsMat = baker.materials().get(new Material(heartRings), heartRings::toDebugFileName); + Material.Baked barkMat = baker.materials().get(new Material(bark), bark::toDebugFileName); + + return BasicBranchBlockBakedModel.bakeBasic(baker, + new BranchModelPart.UnbakedHeartCore(heartMat, barkMat), + new BranchModelPart.UnbakedSleeve(heartMat), + new BranchModelPart.UnbakedCore(ringsMat), + null); + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedRootsModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedRootsModel.java new file mode 100644 index 000000000..f729d08d8 --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedRootsModel.java @@ -0,0 +1,54 @@ +package com.dtteam.dynamictrees.model.blockstate; + +import com.dtteam.dynamictrees.model.baked.BasicRootsBlockBakedModel; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ResolvableModel; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.resources.Identifier; + +import java.util.Optional; + +/** + * Fabric port of the NeoForge {@code UnbakedRootsModel}; deserialized from blockstate JSONs + * with type {@code dynamictrees:roots}. + */ +public record UnbakedRootsModel(Identifier side, Identifier top, boolean opaque) implements CustomUnbakedBlockStateModel { + + public static final String SIDE = "side"; + public static final String TOP = "top"; + public static final String OPAQUE = "opaque"; + public static final String TEXTURES = "textures"; + + private record RootsTextures(Identifier exposedSide, Identifier exposedTop) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Identifier.CODEC.fieldOf(SIDE).forGetter(RootsTextures::exposedSide), + Identifier.CODEC.fieldOf(TOP).forGetter(RootsTextures::exposedTop) + ).apply(i, RootsTextures::new)); + } + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + RootsTextures.CODEC.codec().fieldOf(TEXTURES).forGetter(m -> new RootsTextures(m.side, m.top)), + Codec.BOOL.optionalFieldOf(OPAQUE).forGetter(o -> Optional.of(o.opaque)) + ).apply(i, (textures, opaque) -> new UnbakedRootsModel(textures.exposedSide, textures.exposedTop, opaque.orElse(false)))); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) {} + + @Override + public BlockStateModel bake(ModelBaker baker) { + Material.Baked barkMat = baker.materials().get(new Material(side), side::toDebugFileName); + Material.Baked ringsMat = baker.materials().get(new Material(top), top::toDebugFileName); + + return BasicRootsBlockBakedModel.bakeRoots(baker, barkMat, ringsMat, opaque); + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedRootsMossModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedRootsMossModel.java new file mode 100644 index 000000000..dd41d537c --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedRootsMossModel.java @@ -0,0 +1,51 @@ +package com.dtteam.dynamictrees.model.blockstate; + +import com.dtteam.dynamictrees.model.baked.BasicBranchBlockBakedModel; +import com.dtteam.dynamictrees.model.parts.BranchModelPart; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ResolvableModel; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.resources.Identifier; + +/** + * Fabric port of the NeoForge {@code UnbakedRootsMossModel}; deserialized from blockstate JSONs + * with type {@code dynamictrees:roots_moss}. + */ +public record UnbakedRootsMossModel(Identifier moss) implements CustomUnbakedBlockStateModel { + + public static final String MOSS = "moss"; + public static final String TEXTURES = "textures"; + + private record RootsTextures(Identifier moss) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Identifier.CODEC.fieldOf(MOSS).forGetter(RootsTextures::moss) + ).apply(i, RootsTextures::new)); + } + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + RootsTextures.CODEC.codec().fieldOf(TEXTURES).forGetter(m -> new RootsTextures(m.moss)) + ).apply(i, textures -> new UnbakedRootsMossModel(textures.moss))); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) {} + + @Override + public BlockStateModel bake(ModelBaker baker) { + Material.Baked mossMat = baker.materials().get(new Material(moss), moss::toDebugFileName); + + return BasicBranchBlockBakedModel.bakeBasic(baker, + new BranchModelPart.UnbakedMossCore(mossMat), + new BranchModelPart.UnbakedMossSleeve(mossMat), + new BranchModelPart.UnbakedMossCore(mossMat), + null); + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedSurfaceRootModel.java b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedSurfaceRootModel.java new file mode 100644 index 000000000..6b780e46c --- /dev/null +++ b/fabric/src/main/java/com/dtteam/dynamictrees/model/blockstate/UnbakedSurfaceRootModel.java @@ -0,0 +1,43 @@ +package com.dtteam.dynamictrees.model.blockstate; + +import com.dtteam.dynamictrees.model.baked.SurfaceRootBlockBakedModel; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.client.model.loading.v1.CustomUnbakedBlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ResolvableModel; +import net.minecraft.resources.Identifier; + +/** + * Fabric port of the NeoForge {@code SurfaceRootBlockStateModel.Unbaked}; deserialized from + * blockstate JSONs with type {@code dynamictrees:surface_root}. + */ +public record UnbakedSurfaceRootModel(Identifier barkTexture) implements CustomUnbakedBlockStateModel { + + public static final String TEXTURES = "textures"; + public static final String BARK_TEXTURE = "bark"; + + private record Textures(Identifier bark) { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Identifier.CODEC.fieldOf(BARK_TEXTURE).forGetter(Textures::bark) + ).apply(i, Textures::new)); + } + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + Textures.CODEC.codec().fieldOf(TEXTURES).forGetter(m -> new Textures(m.barkTexture())) + ).apply(i, textures -> new UnbakedSurfaceRootModel(textures.bark()))); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) {} + + @Override + public BlockStateModel bake(ModelBaker baker) { + return SurfaceRootBlockBakedModel.bake(baker, barkTexture); + } +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricClientHelper.java b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricClientHelper.java index 49e245526..ef43ae55f 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricClientHelper.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricClientHelper.java @@ -7,6 +7,7 @@ import com.dtteam.dynamictrees.platform.services.IClientHelper; import com.mojang.blaze3d.platform.NativeImage; import net.minecraft.client.renderer.texture.SpriteContents; +import net.minecraft.util.ARGB; import net.minecraft.client.renderer.texture.TextureAtlasSprite; public class FabricClientHelper implements IClientHelper { @@ -17,7 +18,8 @@ public int getPixelRGBA(TextureAtlasSprite sprite, int x, int y) { SpriteContents contents = sprite.contents(); NativeImage image = contents.originalImage; if (image != null) { - return image.getPixelRGBA(x, y); + // getPixel returns ARGB; convert to the ABGR layout the old getPixelRGBA returned. + return ARGB.toABGR(image.getPixel(x, y)); } return 0; } catch (Exception e) { diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricCompatHelper.java b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricCompatHelper.java index 5ac8c6ef9..2c1935e4b 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricCompatHelper.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricCompatHelper.java @@ -1,13 +1,20 @@ package com.dtteam.dynamictrees.platform; +import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.compat.SereneSeasonsSeasonProvider; import com.dtteam.dynamictrees.platform.services.ICompatHelper; public class FabricCompatHelper implements ICompatHelper { @Override - public void registerSeasonProvider() { - SereneSeasonsSeasonProvider.registerSereneSeasonsProvider(); + public void registerSeasonProvider(String modId) { + switch (modId) { + case DynamicTrees.SERENE_SEASONS -> + SereneSeasonsSeasonProvider.registerSereneSeasonsProvider(); + default -> { + // No other season mods supported on Fabric yet. + } + } } -} \ No newline at end of file +} diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricInteractionHelper.java b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricInteractionHelper.java index ed3a3a0a7..ac4577c5c 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricInteractionHelper.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricInteractionHelper.java @@ -25,11 +25,6 @@ public boolean canToolAxeStrip(ItemStack stack) { return stack.is(ItemTags.AXES); } - @Override - public boolean canToolAxeDig(ItemStack stack) { - return stack.is(ItemTags.AXES); - } - @Override public int setSeedItemEntityLifespan(ItemEntity entityItem, Seed seed) { return 0; @@ -38,14 +33,15 @@ public int setSeedItemEntityLifespan(ItemEntity entityItem, Seed seed) { @Override public boolean blockDestroyByPlayer(BlockState state, Level level, BlockPos pos, Player player, boolean willHarvest, FluidState fluidState) { Block block = state.getBlock(); + ItemStack toolStack = player.getMainHandItem(); if (block instanceof BranchBlock branchBlock) { - return branchBlock.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluidState); + return branchBlock.onDestroyedByPlayer(state, level, pos, player, toolStack, willHarvest, fluidState); } else if (block instanceof TrunkShellBlock trunkShellBlock) { - return trunkShellBlock.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluidState); + return trunkShellBlock.onDestroyedByPlayer(state, level, pos, player, toolStack, willHarvest, fluidState); } else if (block instanceof SoilBlock soilBlock) { return soilBlock.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluidState); } else if (block instanceof PottedSaplingBlock pottedSaplingBlock) { - return pottedSaplingBlock.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluidState); + return pottedSaplingBlock.onDestroyedByPlayer(state, level, pos, player, toolStack, willHarvest, fluidState); } return true; } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricMiscHelper.java b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricMiscHelper.java index c62d584e1..9172874dd 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricMiscHelper.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricMiscHelper.java @@ -1,8 +1,6 @@ package com.dtteam.dynamictrees.platform; -import com.dtteam.dynamictrees.DynamicTrees; import com.dtteam.dynamictrees.platform.services.IMiscHelper; -import com.dtteam.dynamictrees.tree.species.Species; import com.dtteam.dynamictrees.worldgen.IDTBiomeHolderSet; import com.dtteam.dynamictrees.worldgen.holderset.DTBiomeHolderSet; import net.minecraft.server.MinecraftServer; @@ -10,17 +8,6 @@ public class FabricMiscHelper implements IMiscHelper { - public static void debugSpeciesRegistry() { - DynamicTrees.LOG.info("=== Species Registry Debug ==="); - DynamicTrees.LOG.info("Total species count: {}", Species.REGISTRY.getAll().size()); - Species cherry = Species.REGISTRY.get(DynamicTrees.location("cherry")); - DynamicTrees.LOG.info("Cherry species: {} (valid: {})", cherry, cherry != null && cherry.isValid()); - Species oak = Species.REGISTRY.get(DynamicTrees.location("oak")); - DynamicTrees.LOG.info("Oak species: {} (valid: {})", oak, oak != null && oak.isValid()); - DynamicTrees.LOG.info("=== End Species Registry Debug ==="); - } - - @Override public boolean isLevelRestoringBlockSnapshots(Level level) { return false; diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/recipe/DendroPotionRecipeHandler.java b/fabric/src/main/java/com/dtteam/dynamictrees/recipe/DendroPotionRecipeHandler.java index a2f1bbfe8..c317784b6 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/recipe/DendroPotionRecipeHandler.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/recipe/DendroPotionRecipeHandler.java @@ -55,7 +55,7 @@ public static List getAllDendroRecipes() { } public static ItemStack setPotion(ItemStack pStack, String potionName) { - Optional> potion = BuiltInRegistries.POTION.getHolder(ResourceKey.create(Registries.POTION, Identifier.parse(potionName))); + Optional> potion = BuiltInRegistries.POTION.get(ResourceKey.create(Registries.POTION, Identifier.parse(potionName))); potion.ifPresent(holder -> pStack.set(DataComponents.POTION_CONTENTS, new PotionContents(holder))); return pStack; diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/registry/FabricRegistryLoader.java b/fabric/src/main/java/com/dtteam/dynamictrees/registry/FabricRegistryLoader.java index c659f55d3..6bbafc5d7 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/registry/FabricRegistryLoader.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/registry/FabricRegistryLoader.java @@ -4,14 +4,18 @@ import com.dtteam.dynamictrees.recipe.DendroPotionRecipeHandler; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.serialization.MapCodec; -import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.commands.synchronization.ArgumentTypeInfos; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.MutableComponent; +import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityDataRegistry; +import net.minecraft.network.syncher.EntityDataSerializer; +import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; @@ -30,11 +34,8 @@ import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElementType; import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer; -import net.minecraft.world.level.storage.loot.entries.LootPoolEntryType; import net.minecraft.world.level.storage.loot.functions.LootItemFunction; -import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; -import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; import java.util.Set; import java.util.function.Function; @@ -45,19 +46,22 @@ public class FabricRegistryLoader extends RegistryLoader { public static void setup (){ DTRegistries.setup(); - DendroPotionRecipeHandler.getAllDendroRecipes(); + // Dendro brewing recipes are built lazily by MixinPotionBrewing on first use; + // building them here is too early on 26.2 (item components are not bound yet). } @Override public Supplier registerBlock(String name, Function newBlock) { Identifier id = DynamicTrees.location(name); - return ()-> Registry.register(BuiltInRegistries.BLOCK, id, newBlock.apply(id)); + T block = Registry.register(BuiltInRegistries.BLOCK, id, newBlock.apply(id)); + return ()-> block; } @Override public Supplier registerItem(String name, Function newBlock) { Identifier id = DynamicTrees.location(name); - return ()-> Registry.register(BuiltInRegistries.ITEM, id, newBlock.apply(id)); + T item = Registry.register(BuiltInRegistries.ITEM, id, newBlock.apply(id)); + return ()-> item; } @Override @@ -77,13 +81,14 @@ public Supplier registerCreativeTab(String name, Supplier Supplier> registerEntity(String name, EntityType.Builder builder, boolean isTree) { // if (isTree) // builder.setShouldReceiveVelocityUpdates(true).setTrackingRange(512).setUpdateInterval(Integer.MAX_VALUE); - EntityType entityType = Registry.register(BuiltInRegistries.ENTITY_TYPE, DynamicTrees.location(name), builder.build(name)); + Identifier id = DynamicTrees.location(name); + EntityType entityType = Registry.register(BuiltInRegistries.ENTITY_TYPE, id, builder.build(ResourceKey.create(Registries.ENTITY_TYPE, id))); return ()-> entityType; } @Override public Supplier> registerBlockEntity(String name, BlockEntityType.BlockEntitySupplier newBlockEntity, Supplier> validBlocks) { - BlockEntityType entityType = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, DynamicTrees.location(name), new BlockEntityType<>(newBlockEntity, validBlocks.get(), null)); + BlockEntityType entityType = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, DynamicTrees.location(name), new BlockEntityType<>(newBlockEntity, validBlocks.get())); return ()-> entityType; } @@ -108,20 +113,27 @@ public , T extends ArgumentTypeInfo.Template, I ext } @Override - public Supplier registerLootConditionType(String name, MapCodec serializerFactory) { - LootItemConditionType type = Registry.register(BuiltInRegistries.LOOT_CONDITION_TYPE, DynamicTrees.location(name), new LootItemConditionType(serializerFactory)); + public Supplier> registerEntityDataSerializer(String name, Supplier> operator) { + EntityDataSerializer serializer = operator.get(); + FabricEntityDataRegistry.register(DynamicTrees.location(name), serializer); + return ()-> serializer; + } + + @Override + public Supplier> registerLootConditionType(String name, MapCodec serializerFactory) { + MapCodec type = Registry.register(BuiltInRegistries.LOOT_CONDITION_TYPE, DynamicTrees.location(name), serializerFactory); return ()-> type; } @Override - public Supplier registerLootPoolEntryType(String name, MapCodec serializerFactory) { - LootPoolEntryType type = Registry.register(BuiltInRegistries.LOOT_POOL_ENTRY_TYPE, DynamicTrees.location(name), new LootPoolEntryType(serializerFactory)); + public Supplier> registerLootPoolEntryType(String name, MapCodec serializerFactory) { + MapCodec type = Registry.register(BuiltInRegistries.LOOT_POOL_ENTRY_TYPE, DynamicTrees.location(name), serializerFactory); return ()-> type; } @Override - public Supplier> registerLootFunctionType(String name, MapCodec serializerFactory) { - LootItemFunctionType type = Registry.register(BuiltInRegistries.LOOT_FUNCTION_TYPE, DynamicTrees.location(name), new LootItemFunctionType<>(serializerFactory)); + public Supplier> registerLootFunctionType(String name, MapCodec serializerFactory) { + MapCodec type = Registry.register(BuiltInRegistries.LOOT_FUNCTION_TYPE, DynamicTrees.location(name), serializerFactory); return ()-> type; } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/FabricBiomeModifications.java b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/FabricBiomeModifications.java index d8a41d609..9b545f8b7 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/FabricBiomeModifications.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/FabricBiomeModifications.java @@ -72,7 +72,7 @@ private static void removeVanillaTrees(BiomeSelectionContext selectionContext, B for (GenerationStep.Decoration stage : featureCancellations.getDecorationSteps()) { int stageIndex = stage.ordinal(); - List> features = selectionContext.getBiomeRegistryEntry() + List> features = selectionContext.getBiomeHolder() .value() .getGenerationSettings() .features(); @@ -90,9 +90,9 @@ private static void removeVanillaTrees(BiomeSelectionContext selectionContext, B PlacedFeature placedFeature = placedFeatureHolder.value(); - boolean shouldCancel = placedFeature.getFeatures().anyMatch(configuredFeature -> { + boolean shouldCancel = placedFeature.getFeatures().anyMatch(configuredFeatureHolder -> { for (FeatureCanceller featureCanceller : featureCancellations.getCancellers()) { - if (featureCanceller.shouldCancel(configuredFeature, featureCancellations)) { + if (featureCanceller.shouldCancel(configuredFeatureHolder.value(), featureCancellations)) { return true; } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DTBiomeHolderSet.java b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DTBiomeHolderSet.java index b108b8ad7..dbb8613b9 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DTBiomeHolderSet.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DTBiomeHolderSet.java @@ -42,6 +42,11 @@ private Set> getSet() { return tempSet; } + @Override + public boolean isBound() { + return true; + } + @Override public boolean containsKey(ResourceKey biomeKey) { Set> currentSet = this.getSet(); diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DelayedHolderSet.java b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DelayedHolderSet.java index c70ab944e..6713bf116 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DelayedHolderSet.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DelayedHolderSet.java @@ -23,6 +23,11 @@ public DelayedHolderSet(Supplier> holderSetSupplier) { this.holderSetSupplier = holderSetSupplier; } + @Override + public boolean isBound() { + return this.holderSetSupplier.get().isBound(); + } + @Override public Stream> stream() { return this.holderSetSupplier.get().stream(); diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/NameRegexMatchHolderSet.java b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/NameRegexMatchHolderSet.java index f0c897065..74d19bb46 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/NameRegexMatchHolderSet.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/NameRegexMatchHolderSet.java @@ -13,6 +13,6 @@ public NameRegexMatchHolderSet(HolderLookup.RegistryLookup registryLookup, St @Override protected Stream getInput(Holder holder) { - return holder.unwrapKey().stream().map(key -> key.location().toString()); + return holder.unwrapKey().stream().map(key -> key.identifier().toString()); } } diff --git a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/StreamBackedHolderSet.java b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/StreamBackedHolderSet.java index a4b7ad1e0..6a1938eb2 100644 --- a/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/StreamBackedHolderSet.java +++ b/fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/StreamBackedHolderSet.java @@ -1,7 +1,7 @@ package com.dtteam.dynamictrees.worldgen.holderset; import com.mojang.datafixers.util.Either; -import net.minecraft.Util; +import net.minecraft.util.Util; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.tags.TagKey; @@ -11,6 +11,12 @@ import java.util.stream.Collectors; public abstract class StreamBackedHolderSet implements HolderSet { + + @Override + public boolean isBound() { + return true; + } + public List> contents() { return this.stream().collect(Collectors.toList()); } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 27ccc7dd8..b60e965b3 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -22,7 +22,10 @@ "client": [ "com.dtteam.dynamictrees.DynamicTreesFabricClient" ], - "dynamictrees": [] + "dynamictrees": [], + "server": [ + "com.dtteam.dynamictrees.DynamicTreesFabricServer" + ] }, "mixins": [ "${mod_id}.mixins.json", @@ -41,4 +44,3 @@ "forgeconfigapiport": "*" } } - \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 37b51b298..c9c104bfc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,18 +17,18 @@ fabricVersionType=beta # Common java_version=25 -minecraft_version=26.1.2 -minecraft_version_range=[26.1, 26.2) -neo_form_version=26.1.2-1 -forge_config_api_port_version=26.1.4 +minecraft_version=26.2 +minecraft_version_range=[26.2, 26.3) +neo_form_version=26.2-2 +forge_config_api_port_version=26.2.1 # Fabric, see https://fabricmc.net/develop/ for new versions -fabric_version=0.145.3+26.1.1 -fabric_loader_version=0.18.6 -loom_version=1.9.2 +fabric_version=0.155.2+26.2 +fabric_loader_version=0.19.3 +loom_version=1.17.17 # NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=26.1.2.71 +neoforge_version=26.2.0.32-beta neoforge_loader_version_range=[4,) # Gradle diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 23449a2b5..5dd3c0121 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME