Skip to content

Conversation

@Nyfaria
Copy link
Contributor

@Nyfaria Nyfaria commented Jan 24, 2026

No description provided.

Copilot AI review requested due to automatic review settings January 24, 2026 19:45
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds/expands Fabric platform support while refactoring client-only functionality into a separate service (IClientHelper) loaded via ServiceLoader, plus substantial Fabric-side rendering/model-loading/worldgen integration work.

Changes:

  • Split client-only helpers out of IMiscHelper into new IClientHelper + ClientServices service loader wiring.
  • Implement Fabric client rendering/model loading (custom baked models, model loading plugin, falling tree model) and Fabric worldgen hooks (biome modifications + holder set utilities).
  • Update build/publish configuration (Fabric publish plugin, NeoForge publish config changes) and access widener entries.

Reviewed changes

Copilot reviewed 74 out of 75 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
neoforge/src/main/resources/META-INF/services/com.dtteam.dynamictrees.platform.services.IClientHelper Registers NeoForge IClientHelper service implementation.
neoforge/src/main/java/com/dtteam/dynamictrees/platform/NeoForgeMiscHelper.java Removes client-only methods from misc helper.
neoforge/src/main/java/com/dtteam/dynamictrees/platform/NeoForgeClientHelper.java New NeoForge client helper implementing IClientHelper.
neoforge/src/main/java/com/dtteam/dynamictrees/model/loader/ThickBranchBlockModelLoader.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/loader/SurfaceRootBlockModelLoader.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/loader/RootsBlockModelLoader.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/loader/PalmLeavesModelLoader.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/geometry/BranchBlockModelGeometry.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/ThickBranchBlockBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/SurfaceRootBlockBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/SmallPalmLeavesBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/PalmLeavesBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/MediumPalmLeavesBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/LargePalmLeavesBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/BasicRootsBlockBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/BasicBranchBlockBakedModel.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/baked/BakedModelBlockPottedSapling.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/model/QuadManipulator.java Removes NeoForge dist marker annotation usage.
neoforge/src/main/java/com/dtteam/dynamictrees/event/handler/ClientModEventHandler.java Import/style adjustments related to distmarker usage.
neoforge/src/main/java/com/dtteam/dynamictrees/event/handler/ClientGameEventHandler.java Import/style adjustments related to distmarker usage.
neoforge/build.gradle Publishing configuration changes (tokens commented out).
gradle.properties Adds regutils_version property.
fabric/src/main/resources/fabric.mod.json Adds client entrypoint and updates metadata/links/suggests.
fabric/src/main/resources/dynamictrees.fabric.mixins.json Expands mixin list for Fabric behavior parity.
fabric/src/main/resources/META-INF/services/com.dtteam.dynamictrees.platform.services.IClientHelper Registers Fabric IClientHelper service implementation.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/TagsRegexMatchHolderSet.java Adds tag-based regex holder set for Fabric.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/StreamBackedHolderSet.java Adds base HolderSet implementation backed by streams.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/RegexMatchHolderSet.java Adds regex matching holder set using registry lookup streams.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/OrHolderSet.java Adds OR-composition holder set for Fabric.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/NameRegexMatchHolderSet.java Adds name-based regex holder set for Fabric.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DelayedHolderSet.java Adds lazy holder set wrapper for late-bound registries.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/holderset/DTBiomeHolderSet.java Implements Fabric IDTBiomeHolderSet behavior.
fabric/src/main/java/com/dtteam/dynamictrees/worldgen/FabricBiomeModifications.java Adds Fabric biome modification hooks for adding/removing features.
fabric/src/main/java/com/dtteam/dynamictrees/treepack/FabricModFileContainer.java Adjusts resource lookup strategy for Fabric loader containers.
fabric/src/main/java/com/dtteam/dynamictrees/registry/FabricRegistryHandler.java Implements Fabric block/item registration and handler registration.
fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricMiscHelper.java Removes client-only misc methods; adds server tracking + debug helper.
fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricInteractionHelper.java Adds DT block-specific blockDestroyByPlayer delegation.
fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricEventHelper.java Updates Fabric event helper surface for staged resource loader hooks.
fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricConfigHelper.java Improves double config handling for float/double defaults.
fabric/src/main/java/com/dtteam/dynamictrees/platform/FabricClientHelper.java New Fabric IClientHelper implementation (pixel access + falling tree model).
fabric/src/main/java/com/dtteam/dynamictrees/model/baked/ThickBranchBlockBakedModel.java Adds Fabric renderer baked model for thick branches.
fabric/src/main/java/com/dtteam/dynamictrees/model/baked/SurfaceRootBlockBakedModel.java Adds Fabric renderer baked model for surface roots.
fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicRootsBlockBakedModel.java Adds Fabric renderer baked model for underground roots.
fabric/src/main/java/com/dtteam/dynamictrees/model/baked/BasicBranchBlockBakedModel.java Adds Fabric renderer baked model for branches.
fabric/src/main/java/com/dtteam/dynamictrees/model/FallingTreeEntityModelFabric.java Adds Fabric falling tree entity model generation.
fabric/src/main/java/com/dtteam/dynamictrees/model/DTModelLoadingPlugin.java Adds Fabric model loading plugin and baked model caching.
fabric/src/main/java/com/dtteam/dynamictrees/model/BranchBlockUnbakedModel.java Adds Fabric unbaked model implementation for branches.
fabric/src/main/java/com/dtteam/dynamictrees/model/BakedModelBlockPottedSapling.java Adds Fabric wrapper for potted sapling baked model.
fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinSaplingBlock.java Injects sapling growth to replace vanilla saplings with dynamic trees.
fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinMinecraftServer.java Tracks current server instance for Fabric misc helper.
fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinDynamicTreeFeature.java Adds instrumentation/logging around species selector selection.
fabric/src/main/java/com/dtteam/dynamictrees/mixin/MixinBushBlock.java Adjusts survival checks for plants above covered roots.
fabric/src/main/java/com/dtteam/dynamictrees/event/handler/VanillaSaplingEventHandler.java Adds right-click sapling replacement into dynamic saplings on Fabric.
fabric/src/main/java/com/dtteam/dynamictrees/event/handler/ModEventHandler.java Reworks registry/type registration flow for Fabric.
fabric/src/main/java/com/dtteam/dynamictrees/event/handler/CommonEventHandler.java Adds Fabric lifecycle hooks and player block break hook for DT blocks.
fabric/src/main/java/com/dtteam/dynamictrees/config/DTConfigs.java Adds new config entries and fixes numeric literal types.
fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabricClient.java Adds Fabric client entrypoint: renderers, colors, model plugin registration.
fabric/src/main/java/com/dtteam/dynamictrees/DynamicTreesFabric.java Updates Fabric init sequence and adds biome modifications + lifecycle hooks.
fabric/build.gradle Adds mod-publish plugin and datagen run config; configures publishing.
common/src/main/resources/META-INF/dynamictrees.accesswidener Expands access widener for sprite internals + tag provider/appender access.
common/src/main/java/com/dtteam/dynamictrees/tree/family/Family.java Removes distmarker annotations from client-only color fields/methods (now comments).
common/src/main/java/com/dtteam/dynamictrees/platform/services/IMiscHelper.java Removes client-only methods from misc helper interface.
common/src/main/java/com/dtteam/dynamictrees/platform/services/IClientHelper.java Introduces new client helper interface.
common/src/main/java/com/dtteam/dynamictrees/platform/ClientServices.java Adds ServiceLoader-based client service loading.
common/src/main/java/com/dtteam/dynamictrees/model/FallingTreeEntityModelTrackerCache.java Switches falling tree model creation to ClientServices.
common/src/main/java/com/dtteam/dynamictrees/entity/animation/VoidAnimationHandler.java Removes distmarker annotations (now comments).
common/src/main/java/com/dtteam/dynamictrees/entity/animation/PhysicsAnimationHandler.java Removes distmarker annotations (now comments).
common/src/main/java/com/dtteam/dynamictrees/entity/animation/FalloverAnimationHandler.java Removes distmarker annotations (now comments).
common/src/main/java/com/dtteam/dynamictrees/entity/animation/AnimationHandler.java Removes distmarker annotations (now comments).
common/src/main/java/com/dtteam/dynamictrees/client/ThickBranchRingsSprite.java Updates to new SpriteContents.metadata() API usage.
common/src/main/java/com/dtteam/dynamictrees/client/TextureHelper.java Switches pixel sampling to ClientServices.CLIENT.
common/src/main/java/com/dtteam/dynamictrees/client/SoundInstanceHandler.java Removes distmarker annotation (now comment).
common/src/main/java/com/dtteam/dynamictrees/client/BlockColorMultipliers.java Removes distmarker annotation (now comment).
common/src/main/java/com/dtteam/dynamictrees/block/leaves/LeavesProperties.java Removes distmarker annotations (now comments) + client post-init remains.
build.gradle Formatting-only change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +35 to +42
level.removeBlock(pos, false);
ci.cancel();

if (species.isValid()) {
if (DynamicSaplingBlock.canSaplingStay(level, species, pos)) {
species.transitionToTree(level, pos);
}
}
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

This mixin removes the sapling block and cancels vanilla growth unconditionally once a replacer exists. If species is invalid or DynamicSaplingBlock.canSaplingStay is false, the sapling is still deleted and vanilla growth is prevented. Consider only removing/canceling when you are definitely going to transition to a dynamic tree; otherwise return without canceling so vanilla behavior remains intact.

Suggested change
level.removeBlock(pos, false);
ci.cancel();
if (species.isValid()) {
if (DynamicSaplingBlock.canSaplingStay(level, species, pos)) {
species.transitionToTree(level, pos);
}
}
// Only interfere with vanilla behavior if we are definitely transitioning to a dynamic tree
if (species == null || !species.isValid()) {
return;
}
if (!DynamicSaplingBlock.canSaplingStay(level, species, pos)) {
return;
}
level.removeBlock(pos, false);
ci.cancel();
species.transitionToTree(level, pos);

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +213
if (face == Direction.UP || face == Direction.DOWN) {
if (connections[face.get3DDataValue()] < twigRadius && !branchesAround) {
List<BakedQuad> ringQuads = trunksTopRingsQuads[radiusIndex];
if (ringQuads != null) {
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

trunksBotRingsQuads is populated but never used. In emitBlockQuads, ring quads for Direction.DOWN are currently pulled from trunksTopRingsQuads, which will render the wrong UV/orientation for the bottom face. Select trunksTopRingsQuads for UP and trunksBotRingsQuads for DOWN (or compute the correct list based on face).

Copilot uses AI. Check for mistakes.

curseforge {
projectId = "252818"
projectSlug = "dynamictrees" // Required for discord webhook
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

The CurseForge accessToken is now commented out. If the publishMods task is used in CI/release workflows, publishing will fail even when CURSEFORGE_API_KEY is set. Consider restoring this line or conditionally setting it only when a token is present.

Copilot uses AI. Check for mistakes.
Comment on lines 29 to 55
@Inject(method = "getSpeciesSelector", at = @At("RETURN"), remap = false)
private void onGetSpeciesSelector(BiomeDatabase.EntryReader biomeEntry, CallbackInfoReturnable<BiomePropertySelectors.SpeciesSelector> cir) {
if (dynamictrees$loggedCherrySelector) {
return;
}
if (biomeEntry != null && biomeEntry.getBiomeKey() != null) {
String biomeName = biomeEntry.getBiomeKey().location().toString();
if (biomeName.contains("cherry")) {
dynamictrees$loggedCherrySelector = true;
BiomePropertySelectors.SpeciesSelector selector = cir.getReturnValue();
DynamicTrees.LOG.info("Cherry biome species selector type: {} for biome {}",
selector.getClass().getSimpleName(), biomeName);

dynamictrees$debugRandom.setXOR(BlockPos.ZERO);
try {
BiomePropertySelectors.SpeciesSelection selection = selector.getSpecies(
BlockPos.ZERO, Blocks.DIRT.defaultBlockState(), dynamictrees$debugRandom);
Species species = selection.getSpecies();
DynamicTrees.LOG.info("Cherry biome would select species: {} (valid: {}, handled: {})",
species.getRegistryName(), species.isValid(), selection.isHandled());
} catch (Exception e) {
DynamicTrees.LOG.error("Failed to test species selection for cherry biome", e);
}

if (selector instanceof BiomePropertySelectors.RandomSpeciesSelector randomSelector) {
dynamictrees$logRandomSelectorContents(randomSelector);
}
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

This mixin adds production logging and reflective access into BiomePropertySelectors.RandomSpeciesSelector for cherry biomes. Even though it runs once, it still adds runtime overhead, log noise, and a brittle reflection dependency on private field names. Please remove this debug-only instrumentation or gate it behind an explicit debug config/flag so it never runs in normal releases.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +41
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
VanillaSaplingEventHandler.updateEnabled();
FabricMiscHelper.debugSpeciesRegistry();
});
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

FabricMiscHelper.debugSpeciesRegistry() logs multiple INFO lines on every server start. This looks like temporary debugging and will add noise for all users/servers. Please remove it or gate it behind a debug config (e.g., IConfigHelper.DEBUG) / log-level check.

Copilot uses AI. Check for mistakes.
Comment on lines 39 to 51
public boolean blockDestroyByPlayer(BlockState state, Level level, BlockPos pos, Player player, boolean willHarvest, FluidState fluidState) {
return false;
Block block = state.getBlock();
if (block instanceof BranchBlock branchBlock) {
return branchBlock.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluidState);
} else if (block instanceof TrunkShellBlock trunkShellBlock) {
return trunkShellBlock.onDestroyedByPlayer(state, level, pos, player, 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 true;
}
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

For blocks that aren’t one of the Dynamic Trees types, this method returns true without performing any destruction/removal logic. On NeoForge this helper delegates to state.onDestroyedByPlayer(...) for any block, so this behavior is inconsistent across platforms and can become a silent no-op if other code paths start calling it with vanilla/modded blocks. Consider implementing a generic fallback that mimics vanilla block breaking (e.g., calling playerWillDestroy and then replacing the block with the passed fluidState/air).

Copilot uses AI. Check for mistakes.
optional("dynamictreesplus")
}
modrinth {
projectId = "vdjF5PL5"
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

The Modrinth accessToken is now commented out. If the publishMods task is used in CI/release workflows, publishing will fail even when MODRINTH_TOKEN is set. Consider restoring this line or conditionally setting it only when a token is present.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +19
public class MixinBushBlock {

@Shadow protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos){ return false; }
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

@Shadow methods should not provide an implementation body. Having return false; here can cause mixin application errors at runtime. Make this shadowed method abstract (or remove it and use an invoker/accessor pattern if needed).

Suggested change
public class MixinBushBlock {
@Shadow protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos){ return false; }
public abstract class MixinBushBlock {
@Shadow protected abstract boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos);

Copilot uses AI. Check for mistakes.

@Shadow protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos){ return false; }

@Inject(at = @At("HEAD"), cancellable = true, method = "canSurvive (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z")
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

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

The injected method target string includes an extra space ("canSurvive (…"), which means the mixin will not find the target method and will fail to apply or silently do nothing (depending on mixin settings). Remove the space so the method signature matches exactly.

Suggested change
@Inject(at = @At("HEAD"), cancellable = true, method = "canSurvive (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z")
@Inject(at = @At("HEAD"), cancellable = true, method = "canSurvive(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z")

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants