Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,68 +16,14 @@
Feature feature = featureHolder.value();
removeSapling(level, pos);
if (feature.place(level, generator, random, pos)) {
@@ -229,4 +_,64 @@
@@ -229,4 +_,10 @@

private record TwoByTwoSaplingPos(int offsetX, int offsetZ, List<Pair<BlockState, BlockPos>> groundLevelSurroundingBlocks) {
}
+
+ // CraftBukkit start
+ private void setTreeType(Holder<Feature> feature) {
+ if (feature.is(TreeFeatures.OAK) || feature.is(TreeFeatures.OAK_BEES_005)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TREE;
+ } else if (feature.is(TreeFeatures.HUGE_RED_MUSHROOM)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.RED_MUSHROOM;
+ } else if (feature.is(TreeFeatures.HUGE_BROWN_MUSHROOM)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BROWN_MUSHROOM;
+ } else if (feature.is(TreeFeatures.JUNGLE_TREE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.COCOA_TREE;
+ } else if (feature.is(TreeFeatures.JUNGLE_TREE_NO_VINE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SMALL_JUNGLE;
+ } else if (feature.is(TreeFeatures.PINE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_REDWOOD;
+ } else if (feature.is(TreeFeatures.SPRUCE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.REDWOOD;
+ } else if (feature.is(TreeFeatures.ACACIA)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.ACACIA;
+ } else if (feature.is(TreeFeatures.BIRCH) || feature.is(TreeFeatures.BIRCH_BEES_005)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIRCH;
+ } else if (feature.is(TreeFeatures.SUPER_BIRCH_BEES_0002)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_BIRCH;
+ } else if (feature.is(TreeFeatures.SWAMP_OAK)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SWAMP;
+ } else if (feature.is(TreeFeatures.FANCY_OAK) || feature.is(TreeFeatures.FANCY_OAK_BEES_005)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIG_TREE;
+ } else if (feature.is(TreeFeatures.JUNGLE_BUSH)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE_BUSH;
+ } else if (feature.is(TreeFeatures.DARK_OAK)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.DARK_OAK;
+ } else if (feature.is(TreeFeatures.MEGA_SPRUCE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_REDWOOD;
+ } else if (feature.is(TreeFeatures.MEGA_PINE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_PINE;
+ } else if (feature.is(TreeFeatures.MEGA_JUNGLE_TREE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE;
+ } else if (feature.is(TreeFeatures.AZALEA_TREE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.AZALEA;
+ } else if (feature.is(TreeFeatures.MANGROVE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MANGROVE;
+ } else if (feature.is(TreeFeatures.TALL_MANGROVE)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_MANGROVE;
+ } else if (feature.is(TreeFeatures.CHERRY) || feature.is(TreeFeatures.CHERRY_BEES_005)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.CHERRY;
+ } else if (feature.is(TreeFeatures.PALE_OAK) || feature.is(TreeFeatures.PALE_OAK_BONEMEAL)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK;
+ } else if (feature.is(TreeFeatures.PALE_OAK_CREAKING)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK_CREAKING;
+ } else if (feature.is(TreeFeatures.RED_POPLAR)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.RED_POPLAR;
+ } else if (feature.is(TreeFeatures.ORANGE_POPLAR)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.ORANGE_POPLAR;
+ } else if (feature.is(TreeFeatures.YELLOW_POPLAR)) {
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.YELLOW_POPLAR;
+ } else {
+ throw new IllegalArgumentException("Unknown tree generator " + feature);
+ }
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.craftbukkit.CraftTreeType.fromFeature(feature.unwrapKey().orElseThrow());
+ }
+ // CraftBukkit end
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.worldgen.features.TreeFeatures;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
Expand Down Expand Up @@ -162,98 +161,12 @@ public boolean generateTree(Location location, Random random, TreeType treeType,
}

public boolean generateTree(WorldGenLevel access, ChunkGenerator chunkGenerator, BlockPos pos, RandomSource random, TreeType treeType) {
ResourceKey<Feature> gen;
switch (treeType) {
case BIG_TREE:
gen = TreeFeatures.FANCY_OAK;
break;
case BIRCH:
gen = TreeFeatures.BIRCH;
break;
case REDWOOD:
gen = TreeFeatures.SPRUCE;
break;
case TALL_REDWOOD:
gen = TreeFeatures.PINE;
break;
case JUNGLE:
gen = TreeFeatures.MEGA_JUNGLE_TREE;
break;
case SMALL_JUNGLE:
gen = TreeFeatures.JUNGLE_TREE_NO_VINE;
break;
case COCOA_TREE:
gen = TreeFeatures.JUNGLE_TREE;
break;
case JUNGLE_BUSH:
gen = TreeFeatures.JUNGLE_BUSH;
break;
case RED_MUSHROOM:
gen = TreeFeatures.HUGE_RED_MUSHROOM;
break;
case BROWN_MUSHROOM:
gen = TreeFeatures.HUGE_BROWN_MUSHROOM;
break;
case SWAMP:
gen = TreeFeatures.SWAMP_OAK;
break;
case ACACIA:
gen = TreeFeatures.ACACIA;
break;
case DARK_OAK:
gen = TreeFeatures.DARK_OAK;
break;
case MEGA_REDWOOD:
gen = TreeFeatures.MEGA_SPRUCE;
break;
case MEGA_PINE:
gen = TreeFeatures.MEGA_PINE;
break;
case TALL_BIRCH:
gen = TreeFeatures.SUPER_BIRCH_BEES_0002;
break;
case CHORUS_PLANT:
ChorusFlowerBlock.generatePlant(access, pos, random, 8);
return true;
case CRIMSON_FUNGUS:
gen = this.isNormalWorld() ? TreeFeatures.CRIMSON_FUNGUS_PLANTED : TreeFeatures.CRIMSON_FUNGUS; // Paper - Fix async entity add due to fungus trees; if world gen, don't use planted version
break;
case WARPED_FUNGUS:
gen = this.isNormalWorld() ? TreeFeatures.WARPED_FUNGUS_PLANTED : TreeFeatures.WARPED_FUNGUS; // Paper - Fix async entity add due to fungus trees; if world gen, don't use planted version
break;
case AZALEA:
gen = TreeFeatures.AZALEA_TREE;
break;
case MANGROVE:
gen = TreeFeatures.MANGROVE;
break;
case TALL_MANGROVE:
gen = TreeFeatures.TALL_MANGROVE;
break;
case CHERRY:
gen = TreeFeatures.CHERRY;
break;
case PALE_OAK:
gen = TreeFeatures.PALE_OAK;
break;
case PALE_OAK_CREAKING:
gen = TreeFeatures.PALE_OAK_CREAKING;
break;
case RED_POPLAR:
gen = TreeFeatures.RED_POPLAR;
break;
case ORANGE_POPLAR:
gen = TreeFeatures.ORANGE_POPLAR;
break;
case YELLOW_POPLAR:
gen = TreeFeatures.YELLOW_POPLAR;
break;
case TREE:
default:
gen = TreeFeatures.OAK;
break;
if (treeType == TreeType.CHORUS_PLANT) {
ChorusFlowerBlock.generatePlant(access, pos, random, 8);
return true;
}

ResourceKey<Feature> gen = CraftTreeType.toFeature(treeType, this.isNormalWorld());
Holder<Feature> holder = access.registryAccess().lookupOrThrow(Registries.FEATURE).get(gen).orElse(null);
return holder != null && holder.value().place(access, chunkGenerator, random, pos);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.bukkit.craftbukkit;

import java.util.Map;
import net.minecraft.data.worldgen.features.TreeFeatures;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.levelgen.feature.Feature;
import org.bukkit.TreeType;
import org.jspecify.annotations.Nullable;

public final class CraftTreeType {

private static final Map<ResourceKey<Feature>, TreeType> BY_FEATURE = Map.ofEntries(
Map.entry(TreeFeatures.OAK, TreeType.TREE),
Map.entry(TreeFeatures.OAK_BEES_005, TreeType.TREE),
Map.entry(TreeFeatures.FANCY_OAK, TreeType.BIG_TREE),
Map.entry(TreeFeatures.FANCY_OAK_BEES_005, TreeType.BIG_TREE),
Map.entry(TreeFeatures.SPRUCE, TreeType.REDWOOD),
Map.entry(TreeFeatures.PINE, TreeType.TALL_REDWOOD),
Map.entry(TreeFeatures.MEGA_SPRUCE, TreeType.MEGA_REDWOOD),
Map.entry(TreeFeatures.MEGA_PINE, TreeType.MEGA_PINE),
Map.entry(TreeFeatures.BIRCH, TreeType.BIRCH),
Map.entry(TreeFeatures.BIRCH_BEES_005, TreeType.BIRCH),
Map.entry(TreeFeatures.SUPER_BIRCH_BEES_0002, TreeType.TALL_BIRCH),
Map.entry(TreeFeatures.MEGA_JUNGLE_TREE, TreeType.JUNGLE),
Map.entry(TreeFeatures.JUNGLE_TREE_NO_VINE, TreeType.SMALL_JUNGLE),
Map.entry(TreeFeatures.JUNGLE_TREE, TreeType.COCOA_TREE),
Map.entry(TreeFeatures.JUNGLE_BUSH, TreeType.JUNGLE_BUSH),
Map.entry(TreeFeatures.HUGE_RED_MUSHROOM, TreeType.RED_MUSHROOM),
Map.entry(TreeFeatures.HUGE_BROWN_MUSHROOM, TreeType.BROWN_MUSHROOM),
Map.entry(TreeFeatures.SWAMP_OAK, TreeType.SWAMP),
Map.entry(TreeFeatures.ACACIA, TreeType.ACACIA),
Map.entry(TreeFeatures.DARK_OAK, TreeType.DARK_OAK),
Map.entry(TreeFeatures.CRIMSON_FUNGUS, TreeType.CRIMSON_FUNGUS),
Map.entry(TreeFeatures.CRIMSON_FUNGUS_PLANTED, TreeType.CRIMSON_FUNGUS),
Map.entry(TreeFeatures.WARPED_FUNGUS, TreeType.WARPED_FUNGUS),
Map.entry(TreeFeatures.WARPED_FUNGUS_PLANTED, TreeType.WARPED_FUNGUS),
Map.entry(TreeFeatures.AZALEA_TREE, TreeType.AZALEA),
Map.entry(TreeFeatures.MANGROVE, TreeType.MANGROVE),
Map.entry(TreeFeatures.TALL_MANGROVE, TreeType.TALL_MANGROVE),
Map.entry(TreeFeatures.CHERRY, TreeType.CHERRY),
Map.entry(TreeFeatures.CHERRY_BEES_005, TreeType.CHERRY),
Map.entry(TreeFeatures.PALE_OAK, TreeType.PALE_OAK),
Map.entry(TreeFeatures.PALE_OAK_BONEMEAL, TreeType.PALE_OAK),
Map.entry(TreeFeatures.PALE_OAK_CREAKING, TreeType.PALE_OAK_CREAKING),
Map.entry(TreeFeatures.RED_POPLAR, TreeType.RED_POPLAR),
Map.entry(TreeFeatures.ORANGE_POPLAR, TreeType.ORANGE_POPLAR),
Map.entry(TreeFeatures.YELLOW_POPLAR, TreeType.YELLOW_POPLAR)
);

private CraftTreeType() {
}

public static TreeType fromFeature(ResourceKey<Feature> feature) {
TreeType treeType = BY_FEATURE.get(feature);
if (treeType == null) {
throw new IllegalArgumentException("Unknown tree generator " + feature);
}
return treeType;
}

/**
* @return the feature for the tree type, or {@code null} for {@link TreeType#CHORUS_PLANT}
* which isn't placed through a feature
*/
public static @Nullable ResourceKey<Feature> toFeature(TreeType treeType, boolean normalWorld) {
return switch (treeType) {
case TREE -> TreeFeatures.OAK;
case BIG_TREE -> TreeFeatures.FANCY_OAK;
case REDWOOD -> TreeFeatures.SPRUCE;
case TALL_REDWOOD -> TreeFeatures.PINE;
case JUNGLE -> TreeFeatures.MEGA_JUNGLE_TREE;
case SMALL_JUNGLE -> TreeFeatures.JUNGLE_TREE_NO_VINE;
case COCOA_TREE -> TreeFeatures.JUNGLE_TREE;
case JUNGLE_BUSH -> TreeFeatures.JUNGLE_BUSH;
case RED_MUSHROOM -> TreeFeatures.HUGE_RED_MUSHROOM;
case BROWN_MUSHROOM -> TreeFeatures.HUGE_BROWN_MUSHROOM;
case SWAMP -> TreeFeatures.SWAMP_OAK;
case ACACIA -> TreeFeatures.ACACIA;
case DARK_OAK -> TreeFeatures.DARK_OAK;
case MEGA_REDWOOD -> TreeFeatures.MEGA_SPRUCE;
case MEGA_PINE -> TreeFeatures.MEGA_PINE;
case BIRCH -> TreeFeatures.BIRCH;
case TALL_BIRCH -> TreeFeatures.SUPER_BIRCH_BEES_0002;
case CHORUS_PLANT -> null;
case CRIMSON_FUNGUS -> normalWorld ? TreeFeatures.CRIMSON_FUNGUS_PLANTED : TreeFeatures.CRIMSON_FUNGUS; // fix async entity add due to fungus trees; if world gen, don't use planted version
case WARPED_FUNGUS -> normalWorld ? TreeFeatures.WARPED_FUNGUS_PLANTED : TreeFeatures.WARPED_FUNGUS; // fix async entity add due to fungus trees; if world gen, don't use planted version
case AZALEA -> TreeFeatures.AZALEA_TREE;
case MANGROVE -> TreeFeatures.MANGROVE;
case TALL_MANGROVE -> TreeFeatures.TALL_MANGROVE;
case CHERRY -> TreeFeatures.CHERRY;
case PALE_OAK -> TreeFeatures.PALE_OAK;
case PALE_OAK_CREAKING -> TreeFeatures.PALE_OAK_CREAKING;
case RED_POPLAR -> TreeFeatures.RED_POPLAR;
case ORANGE_POPLAR -> TreeFeatures.ORANGE_POPLAR;
case YELLOW_POPLAR -> TreeFeatures.YELLOW_POPLAR;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.bukkit.craftbukkit;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import net.minecraft.data.worldgen.features.TreeFeatures;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.random.Weighted;
import net.minecraft.util.random.WeightedList;
import net.minecraft.world.level.block.grower.TreeGrower;
import net.minecraft.world.level.levelgen.feature.Feature;
import org.bukkit.TreeType;
import org.bukkit.support.environment.Normal;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;

@Normal
public class CraftTreeTypeTest {

// grower features that intentionally share a tree type with another feature
private static final Map<ResourceKey<Feature>, ResourceKey<Feature>> ALIASES = Map.of(
TreeFeatures.OAK_BEES_005, TreeFeatures.OAK,
TreeFeatures.FANCY_OAK_BEES_005, TreeFeatures.FANCY_OAK,
TreeFeatures.BIRCH_BEES_005, TreeFeatures.BIRCH,
TreeFeatures.CHERRY_BEES_005, TreeFeatures.CHERRY,
TreeFeatures.PALE_OAK_BONEMEAL, TreeFeatures.PALE_OAK
);

@SuppressWarnings("unchecked")
public static Stream<ResourceKey<Feature>> growerFeatures() throws ReflectiveOperationException {
List<ResourceKey<Feature>> features = new ArrayList<>();
for (Field growerField : TreeGrower.class.getDeclaredFields()) {
if (!Modifier.isStatic(growerField.getModifiers()) || growerField.getType() != TreeGrower.class) {
continue;
}

TreeGrower grower = (TreeGrower) growerField.get(null);
for (Field listField : TreeGrower.class.getDeclaredFields()) {
if (Modifier.isStatic(listField.getModifiers()) || listField.getType() != WeightedList.class) {
continue;
}

listField.setAccessible(true);
for (Weighted<ResourceKey<Feature>> weighted : ((WeightedList<ResourceKey<Feature>>) listField.get(grower)).unwrap()) {
features.add(weighted.value());
}
}
}
return features.stream().distinct();
}

@ParameterizedTest
@MethodSource("growerFeatures")
public void growerFeatureRoundTrips(ResourceKey<Feature> feature) {
TreeType treeType = assertDoesNotThrow(() -> CraftTreeType.fromFeature(feature), "No TreeType for grower feature " + feature);
assertEquals(ALIASES.getOrDefault(feature, feature), CraftTreeType.toFeature(treeType, true), "TreeType " + treeType + " does not generate grower feature " + feature);
}

@ParameterizedTest
@EnumSource(value = TreeType.class, mode = EnumSource.Mode.EXCLUDE, names = "CHORUS_PLANT")
public void treeTypeRoundTrips(TreeType treeType) {
for (boolean normalWorld : new boolean[] {true, false}) {
assertEquals(treeType, CraftTreeType.fromFeature(CraftTreeType.toFeature(treeType, normalWorld)), "TreeType " + treeType + " does not map back from its feature");
}
}
}
Loading