Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v5
uses: gradle/actions/wrapper-validation@v6
- name: Setup Java
uses: actions/setup-java@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v5
uses: gradle/actions/wrapper-validation@v6
- name: Setup Java
uses: actions/setup-java@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/upload-release-assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v5
uses: gradle/actions/wrapper-validation@v6
- name: Setup Java
uses: actions/setup-java@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
alias(libs.plugins.codecov)
jacoco
id("buildlogic.common")
id("com.gradleup.nmcp.aggregation") version "1.4.4"
id("com.gradleup.nmcp.aggregation") version "1.5.0"
id("xyz.jpenilla.run-paper") version "3.0.2"
}

Expand Down
8 changes: 4 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ worldguard-bukkit = "7.0.15"
griefprevention = "18.0.0"
griefdefender = "2.1.0-SNAPSHOT"
residence = "6.0.0.1"
towny = "0.102.0.12"
towny = "0.103.0.0"
plotsquared = "7.5.12"

# Third party
bstats = "3.2.1"
sparsebitset = "1.3"
parallelgzip = "1.0.5"
adventure = "4.26.1"
checkerqual = "3.54.0"
checkerqual = "4.1.0"
truezip = "6.8.4"
auto-value = "1.11.1"
jsr305 = "3.0.2"
Expand All @@ -31,7 +31,7 @@ antlr4 = "4.13.2"
json-simple = "1.1.1"
jlibnoise = "1.0.0"
jchronic = "0.2.4a"
lz4-java = "1.10.4"
lz4-java = "1.11.0"
commons-cli = "1.11.0"
paperLib = "1.0.8"
paster = "1.1.7"
Expand All @@ -55,7 +55,7 @@ pluginyml = "0.6.0"
mod-publish-plugin = "1.1.0"
grgit = "5.3.3"
shadow = "9.4.1"
paperweight = "2.0.0-beta.19"
paperweight = "2.0.0-SNAPSHOT"
codecov = "0.2.0"


Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
4 changes: 3 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-all.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 10 additions & 21 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
import com.sk89q.worldedit.world.generation.StructureType;
import com.sk89q.worldedit.world.generation.TreeType;
import com.sk89q.worldedit.world.item.ItemType;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
Expand Down Expand Up @@ -113,6 +115,9 @@
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.CoralTreeFeature;
import net.minecraft.world.level.levelgen.feature.TreeFeature;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureStart;
Expand Down Expand Up @@ -918,6 +923,20 @@ public void initializeRegistries() {
}
}

// Trees
HolderLookup.RegistryLookup<PlacedFeature> placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE);
placedFeatureRegistry.listElements()
.filter(feature -> {
var underlyingFeature = feature.value().feature().value().feature();
return underlyingFeature instanceof TreeFeature || underlyingFeature instanceof CoralTreeFeature;
})
.forEach(feature -> {
String key = feature.key().toString();
if (TreeType.REGISTRY.get(key) == null) {
TreeType.REGISTRY.register(key, new TreeType(key));
}
});

// BiomeCategories
Registry<Biome> biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME);
biomeRegistry.getTagNames().forEach(tagKey -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
Expand Down Expand Up @@ -51,6 +52,7 @@
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
import com.sk89q.worldedit.world.generation.StructureType;
import com.sk89q.worldedit.world.generation.TreeType;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import io.papermc.lib.PaperLib;
Expand Down Expand Up @@ -88,6 +90,7 @@
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureStart;
Expand Down Expand Up @@ -679,6 +682,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed
//FAWE end
}

@Override
public boolean generateTree(
final TreeType treeType,
final World world,
final EditSession session,
final BlockVector3 pt
) throws MaxChangedBlocksException {
ServerLevel serverLevel = getServerLevel(world);
ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator();

PlacedFeature placedFeature = serverLevel
.registryAccess()
.registryOrThrow(Registries.PLACED_FEATURE)
.get(ResourceLocation.tryParse(treeType.id()));

FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel);
List<CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
preCaptureStates(serverLevel);
try {
if (!placedFeature.place(
populator,
generator,
serverLevel.random,
new BlockPos(pt.x(), pt.y(), pt.z())
)) {
return null;
}
List<CraftBlockState> placedBlocks = new ArrayList<>(populator.getList());
placedBlocks.addAll(serverLevel.capturedBlockStates.values());
return placedBlocks;
} finally {
postCaptureBlockStates(serverLevel);
}
});

return placeFeatureIntoSession(session, populator, placed);
}

private boolean placeFeatureIntoSession(
final EditSession editSession,
final FaweBlockStateListPopulator populator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
import com.sk89q.worldedit.world.generation.StructureType;
import com.sk89q.worldedit.world.generation.TreeType;
import com.sk89q.worldedit.world.item.ItemType;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
Expand Down Expand Up @@ -113,6 +115,9 @@
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.CoralTreeFeature;
import net.minecraft.world.level.levelgen.feature.TreeFeature;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureStart;
Expand Down Expand Up @@ -917,6 +922,20 @@ public void initializeRegistries() {
}
}

// Trees
HolderLookup.RegistryLookup<PlacedFeature> placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE);
placedFeatureRegistry.listElements()
.filter(feature -> {
var underlyingFeature = feature.value().feature().value().feature();
return underlyingFeature instanceof TreeFeature || underlyingFeature instanceof CoralTreeFeature;
})
.forEach(feature -> {
String key = feature.key().toString();
if (TreeType.REGISTRY.get(key) == null) {
TreeType.REGISTRY.register(key, new TreeType(key));
}
});

// BiomeCategories
Registry<Biome> biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME);
biomeRegistry.getTagNames().forEach(tagKey -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
Expand Down Expand Up @@ -50,6 +51,7 @@
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
import com.sk89q.worldedit.world.generation.StructureType;
import com.sk89q.worldedit.world.generation.TreeType;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import io.papermc.lib.PaperLib;
Expand Down Expand Up @@ -87,6 +89,7 @@
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureStart;
Expand Down Expand Up @@ -678,6 +681,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed
//FAWE end
}

@Override
public boolean generateTree(
final TreeType treeType,
final World world,
final EditSession session,
final BlockVector3 pt
) throws MaxChangedBlocksException {
ServerLevel serverLevel = getServerLevel(world);
ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator();

PlacedFeature placedFeature = serverLevel
.registryAccess()
.registryOrThrow(Registries.PLACED_FEATURE)
.get(ResourceLocation.tryParse(treeType.id()));

FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel);
List<CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
preCaptureStates(serverLevel);
try {
if (!placedFeature.place(
populator,
generator,
serverLevel.random,
new BlockPos(pt.x(), pt.y(), pt.z())
)) {
return null;
Comment on lines +694 to +709
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Generatetree null dereference 🐞 Bug ☼ Reliability

Several PaperweightFaweAdapter.generateTree() implementations fetch a PlacedFeature with
registry.get(...) and then unconditionally call placedFeature.place(...). If the registry lookup
returns null (missing key or parse failure), this will throw a NullPointerException instead of
returning false.
Agent Prompt
### Issue description
`generateTree()` obtains a `PlacedFeature` from the registry and immediately calls `.place(...)` without checking for null, which can crash the operation.

### Issue Context
Other feature generation code paths in the project use `k != null && k.place(...)` and return `false` when missing.

### Fix Focus Areas
- worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java[684-720]
- worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java[685-721]
- worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java[698-734]
- worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java[699-735]

### Suggested fix
After parsing the ResourceLocation and fetching from the registry, return `false` if the parsed key is null or the registry lookup returns null. Only call `.place(...)` when non-null, mirroring how `generateFeature()` handles missing entries.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

}
List<CraftBlockState> placedBlocks = new ArrayList<>(populator.getList());
placedBlocks.addAll(serverLevel.capturedBlockStates.values());
return placedBlocks;
} finally {
postCaptureBlockStates(serverLevel);
}
});

return placeFeatureIntoSession(session, populator, placed);
}

private boolean placeFeatureIntoSession(
final EditSession editSession,
final FaweBlockStateListPopulator populator,
Expand Down
Loading
Loading