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

Large diffs are not rendered by default.

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 @@ -22,41 +22,59 @@
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.Objects.isNull;

@Log4j2
@RequiredArgsConstructor
public class TreeHandler{
@NonNull
private final FallingTreeCommon<?> mod;
private final Map<UUID, CacheSpeed> speedCache = new ConcurrentHashMap<>();
@NonNull
private final Map<UUID, CacheSpeed> speedCache;

@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;

public boolean shouldCancelEvent(@NonNull ILevel level, @NonNull IPlayer player, @NonNull IBlockPos originPos, @NonNull IBlockState originState, @Nullable IBlockEntity originEntity){
@Nullable
private Tree cachedTree = null;

@Nullable
public Tree getTree() throws TreeTooBigException{
if(Objects.isNull(cachedTree)){
cachedTree = mod.getTreeBuilder().getTree(player, level, originPos, originState, originEntity).orElse(null);
}
return cachedTree;
}

public boolean shouldCancelEvent(){
if(!mod.isPlayerInRightState(player)){
return false;
}
if(shouldPreserveTool(player)){
return true;
}
try{
mod.getTreeBuilder().getTree(player, level, originPos, originState, originEntity).isEmpty();
getTree();
}
catch(TreeTooBigException e){
return false;
}
return false;
}

private boolean shouldPreserveTool(@NonNull IPlayer player){
var handItem = player.getMainHandItem();
return mod.getConfiguration().getTools().getDurabilityMode().shouldPreserve(handItem.getDurability());
}

@NonNull
public IBreakAttemptResult breakTree(boolean isCancellable, @NonNull ILevel level, @NonNull IPlayer player, @NonNull IBlockPos originPos, @NonNull IBlockState originState, @Nullable IBlockEntity originEntity){
public IBreakAttemptResult breakTree(boolean isCancellable){
if(!level.isServer()){
return AbortedResult.NOT_SERVER;
}
Expand All @@ -74,12 +92,11 @@ public IBreakAttemptResult breakTree(boolean isCancellable, @NonNull ILevel leve
}

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

var tree = treeOptional.get();
var breakMode = getBreakMode(player.getMainHandItem());
return getBreakingHandler(breakMode).breakTree(isCancellable, player, tree);
}
Expand All @@ -98,25 +115,7 @@ public IBreakAttemptResult breakTree(boolean isCancellable, @NonNull ILevel leve
}

@NonNull
private BreakMode getBreakMode(@NonNull IItemStack itemStack){
return itemStack.getBreakModeFromEnchant()
.orElseGet(() -> mod.getConfiguration().getTrees().getBreakMode());
}

@NonNull
private ITreeBreakingHandler getBreakingHandler(@NonNull BreakMode breakMode){
return switch(breakMode){
case INSTANTANEOUS -> InstantaneousTreeBreakingHandler.getInstance(mod);
case FALL_ITEM -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.withRandomSpread(true, true));
case FALL_ITEM_STRAIGHT -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.straightDown(true, true));
case FALL_BLOCK -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.withRandomSpread(false, true));
case FALL_ALL_BLOCK -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.withRandomSpread(false, false));
case SHIFT_DOWN -> ShiftDownTreeBreakingHandler.getInstance(mod);
};
}

@NonNull
public Optional<Float> getBreakSpeed(@NonNull IPlayer player, @NonNull IBlockPos blockPos, @NonNull IBlockState blockState, float originalSpeed){
public Optional<Float> getBreakSpeed(float originalSpeed){
if(!mod.getConfiguration().getTrees().isTreeBreaking()){
return Optional.empty();
}
Expand All @@ -128,25 +127,48 @@ public Optional<Float> getBreakSpeed(@NonNull IPlayer player, @NonNull IBlockPos
}

var cacheSpeed = speedCache.compute(player.getUUID(), (uuid, speed) -> {
if(isNull(speed) || !speed.isValid(blockPos)){
speed = getSpeed(player, blockPos, blockState, originalSpeed);
if(isNull(speed) || !speed.isValid(originPos)){
speed = getSpeed(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){
private CacheSpeed getSpeed(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())))
return speedMultiplicand <= 0
? null
: Optional.ofNullable(getTree())
.map(tree -> new CacheSpeed(originPos, originalSpeed / ((float) speedMultiplicand * tree.getLogCount())))
.orElse(null);
}
catch(TreeTooBigException e){
return null;
}
}

private boolean shouldPreserveTool(@NonNull IPlayer player){
var handItem = player.getMainHandItem();
return mod.getConfiguration().getTools().getDurabilityMode().shouldPreserve(handItem.getDurability());
}

@NonNull
private BreakMode getBreakMode(@NonNull IItemStack itemStack){
return itemStack.getBreakModeFromEnchant().orElseGet(() -> mod.getConfiguration().getTrees().getBreakMode());
}

@NonNull
private ITreeBreakingHandler getBreakingHandler(@NonNull BreakMode breakMode){
return switch(breakMode){
case INSTANTANEOUS -> InstantaneousTreeBreakingHandler.getInstance(mod);
case FALL_ITEM -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.withRandomSpread(true, true));
case FALL_ITEM_STRAIGHT -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.straightDown(true, true));
case FALL_BLOCK -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.withRandomSpread(false, true));
case FALL_ALL_BLOCK -> FallingAnimationTreeBreakingHandler.getInstance(mod, FallingAnimationTreeBreakingConfig.withRandomSpread(false, false));
case SHIFT_DOWN -> ShiftDownTreeBreakingHandler.getInstance(mod);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package fr.rakambda.fallingtree.common.tree;

import fr.rakambda.fallingtree.common.FallingTreeCommon;
import fr.rakambda.fallingtree.common.utils.CacheSpeed;
import fr.rakambda.fallingtree.common.wrapper.IBlockEntity;
import fr.rakambda.fallingtree.common.wrapper.IBlockPos;
import fr.rakambda.fallingtree.common.wrapper.IBlockState;
import fr.rakambda.fallingtree.common.wrapper.ILevel;
import fr.rakambda.fallingtree.common.wrapper.IPlayer;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Log4j2
@RequiredArgsConstructor
public class TreeHandlerFactory{
@NonNull
private final FallingTreeCommon<?> mod;
private final Map<UUID, CacheSpeed> speedCache = new ConcurrentHashMap<>();

@NonNull
public TreeHandler create(@NonNull ILevel level, @NonNull IPlayer player, @NonNull IBlockPos originPos, @NonNull IBlockState originState, @Nullable IBlockEntity originEntity){
return new TreeHandler(mod, speedCache, player, level, originPos, originState, originEntity);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public IBreakAttemptResult breakTree(boolean isCancellable, @NonNull IPlayer pla
var lootHandler = new LootHandler(wantToBreakCount, mod.getConfiguration().getTrees().getTrunkLootPercentage());
var brokenCount = 0;
var breakablePartsLeft = wantToBreakCount;
var breakableParts = tree.getParts().stream().sorted(mod.getConfiguration().getTrees().getBreakOrder().getComparator()).toList();
var breakableParts = tree.getPartsStream().sorted(mod.getConfiguration().getTrees().getBreakOrder().getComparator()).toList();
for(var part : breakableParts){
if(part.treePartType().isBreakable()){
if(breakablePartsLeft == 0){
Expand Down
Loading
Loading