Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -5,6 +5,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
Expand All @@ -27,6 +28,7 @@ public class Tree{
@Getter
private final Set<TreePart> parts = new LinkedHashSet<>();
private final Map<TreePartType, Integer> partCounts = new LinkedHashMap<>();
private final ExtremumBlocks extremumBlocks = new ExtremumBlocks();

public void addPart(@NonNull TreePart treePart){
parts.add(treePart);
Expand All @@ -36,6 +38,7 @@ public void addPart(@NonNull TreePart treePart){
}
return value + 1;
});
extremumBlocks.update(treePart);
}

public void removePartsHigherThan(int y, @NonNull TreePartType partType){
Expand Down Expand Up @@ -99,18 +102,78 @@ public int getLogCount(){
return getPartCount(TreePartType.LOG);
}

private static class ExtremumBlocks {
Comment thread
Rakambda marked this conversation as resolved.
Outdated

@Nullable
private TreePart topMostLog;

@Nullable
private TreePart bottomMostLog;

@Nullable
private TreePart logStart;

private void updateTopMostLog(@NonNull TreePart candidate){
if(this.topMostLog == null){
this.topMostLog = candidate;
return;
}

if(candidate.treePartType().isBreakable()
&& candidate.treePartType().isLog()
&& candidate.blockPos().getY() > this.topMostLog.blockPos().getY()){
this.topMostLog = candidate;
}
}

private void updateBottomMostLog(@NonNull TreePart candidate){
if(this.bottomMostLog == null){
this.bottomMostLog = candidate;
return;
}

if(candidate.treePartType().isBreakable()
&& candidate.treePartType().isLog()
&& candidate.blockPos().getY() < this.bottomMostLog.blockPos().getY()){
this.bottomMostLog = candidate;
}
}

private void updateLogStart(@NonNull TreePart candidate) {
if(this.logStart == null && candidate.treePartType() == TreePartType.LOG_START){
this.logStart = candidate;
}
}

protected void update(@NonNull TreePart candidate){
updateLogStart(candidate);
updateTopMostLog(candidate);
updateBottomMostLog(candidate);
}

public Optional<TreePart> getTopMostLog(){
return Optional.ofNullable(topMostLog);
}

public Optional<TreePart> getBottomMostLog(){
return Optional.ofNullable(bottomMostLog);
}

public Optional<TreePart> getLogStart(){
return Optional.ofNullable(logStart);
}
}

@NonNull
public Optional<IBlockPos> getTopMostLog(){
return getBreakableLogs().stream()
.map(TreePart::blockPos)
.max(comparingInt(IBlockPos::getY));
return extremumBlocks.getTopMostLog()
.map(TreePart::blockPos);
}

@NonNull
public Optional<IBlockPos> getBottomMostLog(){
return getBreakableLogs().stream()
.map(TreePart::blockPos)
.min(comparingInt(IBlockPos::getY));
return extremumBlocks.getBottomMostLog()
.map(TreePart::blockPos);
}

@NonNull
Expand All @@ -136,8 +199,6 @@ public Collection<TreePart> getMangroveRoots(){

@NonNull
public Optional<TreePart> getStart(){
return getParts().stream()
.filter(part -> part.treePartType() == TreePartType.LOG_START)
.findFirst();
return extremumBlocks.getLogStart();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,67 +34,143 @@ public class TreeHandler{
private final FallingTreeCommon<?> mod;
private final Map<UUID, CacheSpeed> speedCache = new ConcurrentHashMap<>();

public boolean shouldCancelEvent(@NonNull ILevel level, @NonNull IPlayer player, @NonNull IBlockPos originPos, @NonNull IBlockState originState, @Nullable IBlockEntity originEntity){
if(!mod.isPlayerInRightState(player)){
public class TreeHandlerState{
@NonNull
private final IPlayer player;
@NonNull
private final ILevel level;
@NonNull
private final IBlockPos originPos;
@NonNull
private final IBlockState originState;
@Nullable
private final IBlockEntity originEntity;

@Nullable
private Tree treeCache = null;

public TreeHandlerState(@NonNull IPlayer player, @NonNull ILevel level, @NonNull IBlockPos originPos, @NonNull IBlockState originState, @Nullable IBlockEntity originEntity){
this.player = player;
this.level = level;
this.originPos = originPos;
this.originState = originState;
this.originEntity = originEntity;
}

public Tree getTree() throws TreeTooBigException{
if(this.treeCache == null){
this.treeCache = mod.getTreeBuilder().getTree(player, level, originPos, originState, originEntity).orElse(null);
}

return this.treeCache;
}

public boolean shouldCancelEvent(){
if(!mod.isPlayerInRightState(player)){
return false;
}
if(shouldPreserveTool(player)){
return true;
}
try{
this.getTree();
}
catch(TreeTooBigException e){
return false;
}
return false;
}
if(shouldPreserveTool(player)){
return true;

@NonNull
public IBreakAttemptResult breakTree(boolean isCancellable){
if(!level.isServer()){
return AbortedResult.NOT_SERVER;
}
if(!mod.getConfiguration().getTrees().isTreeBreaking()){
return AbortedResult.NOT_ENABLED;
}

if(!mod.checkForceToolUsage(player, level, originPos)){
mod.notifyPlayer(player, mod.translate("chat.fallingtree.force_tool_usage", mod.getConfiguration().getTrees().getMaxScanSize()));
return AbortedResult.REQUIRED_TOOL_ABSENT;
}

if(!mod.isPlayerInRightState(player)){
return AbortedResult.INVALID_PLAYER_STATE;
}

try{
var tree = this.getTree();
if(tree == null){
return AbortedResult.NO_SUCH_TREE;
}

var breakMode = getBreakMode(player.getMainHandItem());
return getBreakingHandler(breakMode).breakTree(isCancellable, player, tree);
}
catch(TreeTooBigException e){
mod.notifyPlayer(player, mod.translate("chat.fallingtree.tree_too_big", mod.getConfiguration().getTrees().getMaxScanSize()));
return AbortedResult.TREE_TOO_BIG_SCAN;
}
catch(BreakTreeTooSmallException e){
// mod.notifyPlayer(player, mod.translate("chat.fallingtree.break_tree_too_small", mod.getConfiguration().getTrees().getMinSize()));
return AbortedResult.TREE_TOO_SMALL_BREAK;
}
catch(BreakTreeTooBigException e){
mod.notifyPlayer(player, mod.translate("chat.fallingtree.break_tree_too_big", mod.getConfiguration().getTrees().getMaxSize()));
return AbortedResult.TREE_TOO_BIG_BREAK;
}
}
try{
mod.getTreeBuilder().getTree(player, level, originPos, originState, originEntity).isEmpty();

@NonNull
public Optional<Float> getBreakSpeed(float originalSpeed){
if(!mod.getConfiguration().getTrees().isTreeBreaking()){
return Optional.empty();
}
if(!getBreakMode(player.getMainHandItem()).isApplySpeedMultiplier()){
return Optional.empty();
}
if(!mod.isPlayerInRightState(player)){
return Optional.empty();
}

var cacheSpeed = speedCache.compute(player.getUUID(), (uuid, speed) -> {
if(isNull(speed) || !speed.isValid(originPos)){
speed = getSpeed(originalSpeed);
}
return speed;
});
return Optional.ofNullable(cacheSpeed).map(CacheSpeed::getSpeed);
}
catch(TreeTooBigException e){
return false;

@Nullable
private CacheSpeed getSpeed(float originalSpeed){
var speedMultiplicand = mod.getConfiguration().getTools().getSpeedMultiplicand();
try{
return speedMultiplicand <= 0 ? null :
Optional.ofNullable(this.getTree())
.map(tree -> new CacheSpeed(originPos, originalSpeed / ((float) speedMultiplicand * tree.getLogCount())))
.orElse(null);
}
catch(TreeTooBigException e){
return null;
}
}
return false;
}

private boolean shouldPreserveTool(@NonNull IPlayer player){
var handItem = player.getMainHandItem();
return mod.getConfiguration().getTools().getDurabilityMode().shouldPreserve(handItem.getDurability());
}
@NonNull
public TreeHandlerState create(@NonNull ILevel level, @NonNull IPlayer player, @NonNull IBlockPos originPos, @NonNull IBlockState originState) {
return create(level, player, originPos, originState, null);
}


@NonNull
public IBreakAttemptResult breakTree(boolean isCancellable, @NonNull ILevel level, @NonNull IPlayer player, @NonNull IBlockPos originPos, @NonNull IBlockState originState, @Nullable IBlockEntity originEntity){
if(!level.isServer()){
return AbortedResult.NOT_SERVER;
}
if(!mod.getConfiguration().getTrees().isTreeBreaking()){
return AbortedResult.NOT_ENABLED;
}

if(!mod.checkForceToolUsage(player, level, originPos)){
mod.notifyPlayer(player, mod.translate("chat.fallingtree.force_tool_usage", mod.getConfiguration().getTrees().getMaxScanSize()));
return AbortedResult.REQUIRED_TOOL_ABSENT;
}

if(!mod.isPlayerInRightState(player)){
return AbortedResult.INVALID_PLAYER_STATE;
}

try{
var treeOptional = mod.getTreeBuilder().getTree(player, level, originPos, originState, originEntity);
if(treeOptional.isEmpty()){
return AbortedResult.NO_SUCH_TREE;
}

var tree = treeOptional.get();
var breakMode = getBreakMode(player.getMainHandItem());
return getBreakingHandler(breakMode).breakTree(isCancellable, player, tree);
}
catch(TreeTooBigException e){
mod.notifyPlayer(player, mod.translate("chat.fallingtree.tree_too_big", mod.getConfiguration().getTrees().getMaxScanSize()));
return AbortedResult.TREE_TOO_BIG_SCAN;
}
catch(BreakTreeTooSmallException e){
// mod.notifyPlayer(player, mod.translate("chat.fallingtree.break_tree_too_small", mod.getConfiguration().getTrees().getMinSize()));
return AbortedResult.TREE_TOO_SMALL_BREAK;
}
catch(BreakTreeTooBigException e){
mod.notifyPlayer(player, mod.translate("chat.fallingtree.break_tree_too_big", mod.getConfiguration().getTrees().getMaxSize()));
return AbortedResult.TREE_TOO_BIG_BREAK;
}
public TreeHandlerState create(@NonNull ILevel level, @NonNull IPlayer player, @NonNull IBlockPos originPos, @NonNull IBlockState originState, @Nullable IBlockEntity originEntity) {
return new TreeHandlerState(player, level, originPos, originState, originEntity);
}

@NonNull
Expand All @@ -114,39 +190,4 @@ private ITreeBreakingHandler getBreakingHandler(@NonNull BreakMode breakMode){
case SHIFT_DOWN -> ShiftDownTreeBreakingHandler.getInstance(mod);
};
}

@NonNull
public Optional<Float> getBreakSpeed(@NonNull IPlayer player, @NonNull IBlockPos blockPos, @NonNull IBlockState blockState, float originalSpeed){
if(!mod.getConfiguration().getTrees().isTreeBreaking()){
return Optional.empty();
}
if(!getBreakMode(player.getMainHandItem()).isApplySpeedMultiplier()){
return Optional.empty();
}
if(!mod.isPlayerInRightState(player)){
return Optional.empty();
}

var cacheSpeed = speedCache.compute(player.getUUID(), (uuid, speed) -> {
if(isNull(speed) || !speed.isValid(blockPos)){
speed = getSpeed(player, blockPos, blockState, originalSpeed);
}
return speed;
});
return Optional.ofNullable(cacheSpeed).map(CacheSpeed::getSpeed);
}

@Nullable
private CacheSpeed getSpeed(@NonNull IPlayer player, @NonNull IBlockPos pos, @NonNull IBlockState blockState, float originalSpeed){
var speedMultiplicand = mod.getConfiguration().getTools().getSpeedMultiplicand();
try{
return speedMultiplicand <= 0 ? null :
mod.getTreeBuilder().getTree(player, player.getLevel(), pos, blockState, null)
.map(tree -> new CacheSpeed(pos, originalSpeed / ((float) speedMultiplicand * tree.getLogCount())))
.orElse(null);
}
catch(TreeTooBigException e){
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public boolean beforeBlockBreak(Level level, Player player, BlockPos blockPos, B
return true;
}

return !mod.getTreeHandler().shouldCancelEvent(wrappedLevel, wrappedPlayer, wrappedPos, wrappedState, wrappedEntity);
return !mod.getTreeHandler().create(wrappedLevel, wrappedPlayer, wrappedPos, wrappedState, wrappedEntity).shouldCancelEvent();
}

@Override
Expand All @@ -50,6 +50,6 @@ public void afterBlockBreak(Level level, Player player, BlockPos blockPos, Block
var wrappedState = new BlockStateWrapper(blockState);
var wrappedEntity = Optional.ofNullable(blockEntity).map(BlockEntityWrapper::new).orElse(null);

mod.getTreeHandler().breakTree(false, wrappedLevel, wrappedPlayer, wrappedPos, wrappedState, wrappedEntity);
mod.getTreeHandler().create(wrappedLevel, wrappedPlayer, wrappedPos, wrappedState, wrappedEntity).breakTree(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void onBreakSpeed(@Nonnull PlayerEvent.BreakSpeed event){
var wrappedPos = new BlockPosWrapper(optionalPos.get());
var wrappedState = new BlockStateWrapper(event.getState());

var result = mod.getTreeHandler().getBreakSpeed(wrappedPlayer, wrappedPos, wrappedState, event.getNewSpeed());
var result = mod.getTreeHandler().create(wrappedPlayer.getLevel(), wrappedPlayer, wrappedPos, wrappedState).getBreakSpeed(event.getNewSpeed());
if(result.isEmpty()){
return;
}
Expand All @@ -48,11 +48,13 @@ public boolean onBlockBreakEvent(@Nonnull BlockEvent.BreakEvent event){
var wrappedState = new BlockStateWrapper(event.getState());
var wrappedEntity = wrappedLevel.getBlockEntity(wrappedPos);

if(mod.getTreeHandler().shouldCancelEvent(wrappedLevel, wrappedPlayer, wrappedPos, wrappedState, wrappedEntity)){
final var treeHandler = mod.getTreeHandler().create(wrappedLevel, wrappedPlayer, wrappedPos, wrappedState, wrappedEntity);

if(treeHandler.shouldCancelEvent()){
return true;
}

var result = mod.getTreeHandler().breakTree(true, wrappedLevel, wrappedPlayer, wrappedPos, wrappedState, wrappedEntity);
var result = treeHandler.breakTree(true);
return result.shouldCancel();
}
}
Loading