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
137 changes: 18 additions & 119 deletions common/src/main/java/fr/rakambda/fallingtree/common/tree/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,142 +2,41 @@

import fr.rakambda.fallingtree.common.wrapper.IBlockPos;
import fr.rakambda.fallingtree.common.wrapper.ILevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jspecify.annotations.NonNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import static java.util.Comparator.comparingInt;
import static java.util.Objects.isNull;
import static java.util.stream.Collectors.toSet;
import java.util.stream.Stream;

@RequiredArgsConstructor
public class Tree{
@Getter
@NonNull
private final ILevel level;
@Getter
@NonNull
private final IBlockPos hitPos;
@Getter
private final Set<TreePart> parts = new LinkedHashSet<>();
private final Map<TreePartType, Integer> partCounts = new LinkedHashMap<>();
public interface Tree{

public void addPart(@NonNull TreePart treePart){
parts.add(treePart);
partCounts.compute(treePart.treePartType(), (key, value) -> {
if(isNull(value)){
return 1;
}
return value + 1;
});
}
int getBreakableCount();

public void removePartsHigherThan(int y, @NonNull TreePartType partType){
parts.removeIf(part -> {
if(part.treePartType() == partType && part.blockPos().getY() > y){
decrementPartCount(partType);
return true;
}
return false;
});
}
@NonNull Optional<TreePart> getLastSequencePart();

public int getBreakableCount(){
return Arrays.stream(TreePartType.getValues())
.filter(TreePartType::isBreakable)
.mapToInt(this::getPartCount)
.sum();
}
@NonNull Optional<TreePart> getLastSequenceLogPart();

private int getPartCount(@NonNull TreePartType treePartType){
return partCounts.computeIfAbsent(treePartType, key -> 0);
}
@NonNull Collection<TreePart> getBreakableLogs();

public int getSize(){
return partCounts.values().stream().mapToInt(i -> i).sum();
}
@NonNull Collection<TreePart> getBreakableParts();

private void decrementPartCount(@NonNull TreePartType partType){
partCounts.computeIfPresent(partType, (type, count) -> Math.max(0, count - 1));
}
int getLogCount();

@NonNull
public Optional<TreePart> getLastSequencePart(){
return getParts().stream()
.max(comparingInt(TreePart::sequence));
}
@NonNull Optional<IBlockPos> getTopMostLog();

@NonNull
public Optional<TreePart> getLastSequenceLogPart(){
return getParts().stream()
.filter(part -> part.treePartType().isLog())
.max(comparingInt(TreePart::sequence));
}
@NonNull Optional<IBlockPos> getBottomMostLog();

@NonNull
public Collection<TreePart> getBreakableLogs(){
return getParts().stream()
.filter(part -> part.treePartType().isLog())
.filter(part -> part.treePartType().isBreakable())
.collect(toSet());
}
@NonNull Optional<IBlockPos> getTopMostPart();

@NonNull
public Collection<TreePart> getBreakableParts(){
return getParts().stream()
.filter(part -> part.treePartType().isBreakable())
.collect(toSet());
}
@NonNull Collection<TreePart> getNetherWarts();

public int getLogCount(){
return getPartCount(TreePartType.LOG);
}
@NonNull Collection<TreePart> getMangroveRoots();

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

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

@NonNull
private Optional<IBlockPos> getTopMostPart(){
return getParts().stream()
.map(TreePart::blockPos)
.max(comparingInt(IBlockPos::getY));
}
ILevel getLevel();

@NonNull
public Collection<TreePart> getNetherWarts(){
return getParts().stream()
.filter(part -> part.treePartType() == TreePartType.NETHER_WART)
.collect(toSet());
}

@NonNull
public Collection<TreePart> getMangroveRoots(){
return getParts().stream()
.filter(part -> part.treePartType() == TreePartType.MANGROVE_ROOTS)
.collect(toSet());
}

@NonNull
public Optional<TreePart> getStart(){
return getParts().stream()
.filter(part -> part.treePartType() == TreePartType.LOG_START)
.findFirst();
}
IBlockPos getHitPos();
}

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;
}
}
}
Loading