mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2025-10-29 11:52:22 +00:00
Compare commits
3 Commits
b60e478aec
...
b91fa2cf37
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b91fa2cf37 | ||
|
|
6acc4b1ec6 | ||
|
|
41b5667cd4 |
@ -1,7 +1,8 @@
|
||||
Version 2.2.040
|
||||
Fixed hover component and action bar messages not working for 1.21.6 and 1.21.7
|
||||
Fixed bug where entries of mctop could be duplicated when using FlatFile
|
||||
Fixed bug where a party leader could leave a party and the party would be left without a party leader
|
||||
Fixed a bug where EcoEnchants or other similar plugins could cause an infinite loop within mcMMO
|
||||
(Codebase) Updated Adventure Libs
|
||||
Fixed a bug where EcoEnchants and similar plugins could cause an infinite loop within mcMMO during TreeFeller and other abilities
|
||||
Added 'Happy_Ghast' to experience.yml for combat XP
|
||||
Added 'Ghastling' to experience.yml for combat XP
|
||||
Updated Japanese (ja_JP) translation (Thanks ryota-abe)
|
||||
|
||||
2
pom.xml
2
pom.xml
@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.gmail.nossr50.mcMMO</groupId>
|
||||
<artifactId>mcMMO</artifactId>
|
||||
<version>2.2.040-SNAPSHOT</version>
|
||||
<version>2.2.041-SNAPSHOT</version>
|
||||
<name>mcMMO</name>
|
||||
<url>https://github.com/mcMMO-Dev/mcMMO</url>
|
||||
<scm>
|
||||
|
||||
@ -21,11 +21,11 @@ import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@ -36,9 +36,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
public static final String IGNORED = "IGNORED";
|
||||
public static final String LEGACY_INVALID_OLD_USERNAME = "_INVALID_OLD_USERNAME_'";
|
||||
private final @NotNull EnumMap<PrimarySkillType, List<PlayerStat>> playerStatHash = new EnumMap<>(
|
||||
private final @NotNull EnumMap<PrimarySkillType, List<PlayerStat>> leaderboardMap = new EnumMap<>(
|
||||
PrimarySkillType.class);
|
||||
private final @NotNull List<PlayerStat> powerLevels = new ArrayList<>();
|
||||
private @NotNull List<PlayerStat> powerLevels = new ArrayList<>();
|
||||
private long lastUpdate = 0;
|
||||
private final @NotNull String usersFilePath;
|
||||
private final @NotNull Logger logger;
|
||||
@ -562,7 +562,7 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
|
||||
updateLeaderboards();
|
||||
List<PlayerStat> statsList =
|
||||
primarySkillType == null ? powerLevels : playerStatHash.get(primarySkillType);
|
||||
primarySkillType == null ? powerLevels : leaderboardMap.get(primarySkillType);
|
||||
int fromIndex = (Math.max(pageNumber, 1) - 1) * statsPerPage;
|
||||
|
||||
return statsList.subList(Math.min(fromIndex, statsList.size()),
|
||||
@ -575,10 +575,9 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
HashMap<PrimarySkillType, Integer> skills = new HashMap<>();
|
||||
|
||||
for (PrimarySkillType skill : SkillTools.NON_CHILD_SKILLS) {
|
||||
skills.put(skill, getPlayerRank(playerName, playerStatHash.get(skill)));
|
||||
skills.put(skill, getPlayerRank(playerName, leaderboardMap.get(skill)));
|
||||
}
|
||||
|
||||
//TODO: Gross
|
||||
skills.put(null, getPlayerRank(playerName, powerLevels));
|
||||
|
||||
return skills;
|
||||
@ -1039,25 +1038,24 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
}
|
||||
|
||||
lastUpdate = System.currentTimeMillis(); // Log when the last update was run
|
||||
powerLevels.clear(); // Clear old values from the power levels
|
||||
|
||||
// Initialize lists
|
||||
List<PlayerStat> mining = new ArrayList<>();
|
||||
List<PlayerStat> woodcutting = new ArrayList<>();
|
||||
List<PlayerStat> herbalism = new ArrayList<>();
|
||||
List<PlayerStat> excavation = new ArrayList<>();
|
||||
List<PlayerStat> acrobatics = new ArrayList<>();
|
||||
List<PlayerStat> repair = new ArrayList<>();
|
||||
List<PlayerStat> swords = new ArrayList<>();
|
||||
List<PlayerStat> axes = new ArrayList<>();
|
||||
List<PlayerStat> archery = new ArrayList<>();
|
||||
List<PlayerStat> unarmed = new ArrayList<>();
|
||||
List<PlayerStat> taming = new ArrayList<>();
|
||||
List<PlayerStat> fishing = new ArrayList<>();
|
||||
List<PlayerStat> alchemy = new ArrayList<>();
|
||||
List<PlayerStat> crossbows = new ArrayList<>();
|
||||
List<PlayerStat> tridents = new ArrayList<>();
|
||||
List<PlayerStat> maces = new ArrayList<>();
|
||||
TreeSet<PlayerStat> powerLevelStats = new TreeSet<>();
|
||||
TreeSet<PlayerStat> mining = new TreeSet<>();
|
||||
TreeSet<PlayerStat> woodcutting = new TreeSet<>();
|
||||
TreeSet<PlayerStat> herbalism = new TreeSet<>();
|
||||
TreeSet<PlayerStat> excavation = new TreeSet<>();
|
||||
TreeSet<PlayerStat> acrobatics = new TreeSet<>();
|
||||
TreeSet<PlayerStat> repair = new TreeSet<>();
|
||||
TreeSet<PlayerStat> swords = new TreeSet<>();
|
||||
TreeSet<PlayerStat> axes = new TreeSet<>();
|
||||
TreeSet<PlayerStat> archery = new TreeSet<>();
|
||||
TreeSet<PlayerStat> unarmed = new TreeSet<>();
|
||||
TreeSet<PlayerStat> taming = new TreeSet<>();
|
||||
TreeSet<PlayerStat> fishing = new TreeSet<>();
|
||||
TreeSet<PlayerStat> alchemy = new TreeSet<>();
|
||||
TreeSet<PlayerStat> crossbows = new TreeSet<>();
|
||||
TreeSet<PlayerStat> tridents = new TreeSet<>();
|
||||
TreeSet<PlayerStat> maces = new TreeSet<>();
|
||||
|
||||
BufferedReader in = null;
|
||||
String playerName = null;
|
||||
@ -1106,7 +1104,7 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
skills.get(PrimarySkillType.TRIDENTS));
|
||||
powerLevel += putStat(maces, playerName, skills.get(PrimarySkillType.MACES));
|
||||
|
||||
putStat(powerLevels, playerName, powerLevel);
|
||||
putStat(powerLevelStats, playerName, powerLevel);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.severe(
|
||||
@ -1125,42 +1123,23 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
|
||||
}
|
||||
|
||||
SkillComparator c = new SkillComparator();
|
||||
|
||||
mining.sort(c);
|
||||
woodcutting.sort(c);
|
||||
repair.sort(c);
|
||||
unarmed.sort(c);
|
||||
herbalism.sort(c);
|
||||
excavation.sort(c);
|
||||
archery.sort(c);
|
||||
swords.sort(c);
|
||||
axes.sort(c);
|
||||
acrobatics.sort(c);
|
||||
taming.sort(c);
|
||||
fishing.sort(c);
|
||||
alchemy.sort(c);
|
||||
crossbows.sort(c);
|
||||
tridents.sort(c);
|
||||
maces.sort(c);
|
||||
powerLevels.sort(c);
|
||||
|
||||
playerStatHash.put(PrimarySkillType.MINING, mining);
|
||||
playerStatHash.put(PrimarySkillType.WOODCUTTING, woodcutting);
|
||||
playerStatHash.put(PrimarySkillType.REPAIR, repair);
|
||||
playerStatHash.put(PrimarySkillType.UNARMED, unarmed);
|
||||
playerStatHash.put(PrimarySkillType.HERBALISM, herbalism);
|
||||
playerStatHash.put(PrimarySkillType.EXCAVATION, excavation);
|
||||
playerStatHash.put(PrimarySkillType.ARCHERY, archery);
|
||||
playerStatHash.put(PrimarySkillType.SWORDS, swords);
|
||||
playerStatHash.put(PrimarySkillType.AXES, axes);
|
||||
playerStatHash.put(PrimarySkillType.ACROBATICS, acrobatics);
|
||||
playerStatHash.put(PrimarySkillType.TAMING, taming);
|
||||
playerStatHash.put(PrimarySkillType.FISHING, fishing);
|
||||
playerStatHash.put(PrimarySkillType.ALCHEMY, alchemy);
|
||||
playerStatHash.put(PrimarySkillType.CROSSBOWS, crossbows);
|
||||
playerStatHash.put(PrimarySkillType.TRIDENTS, tridents);
|
||||
playerStatHash.put(PrimarySkillType.MACES, maces);
|
||||
powerLevels = List.copyOf(powerLevelStats);
|
||||
leaderboardMap.put(PrimarySkillType.MINING, List.copyOf(mining));
|
||||
leaderboardMap.put(PrimarySkillType.WOODCUTTING, List.copyOf(woodcutting));
|
||||
leaderboardMap.put(PrimarySkillType.REPAIR, List.copyOf(repair));
|
||||
leaderboardMap.put(PrimarySkillType.UNARMED, List.copyOf(unarmed));
|
||||
leaderboardMap.put(PrimarySkillType.HERBALISM, List.copyOf(herbalism));
|
||||
leaderboardMap.put(PrimarySkillType.EXCAVATION, List.copyOf(excavation));
|
||||
leaderboardMap.put(PrimarySkillType.ARCHERY, List.copyOf(archery));
|
||||
leaderboardMap.put(PrimarySkillType.SWORDS, List.copyOf(swords));
|
||||
leaderboardMap.put(PrimarySkillType.AXES, List.copyOf(axes));
|
||||
leaderboardMap.put(PrimarySkillType.ACROBATICS, List.copyOf(acrobatics));
|
||||
leaderboardMap.put(PrimarySkillType.TAMING, List.copyOf(taming));
|
||||
leaderboardMap.put(PrimarySkillType.FISHING, List.copyOf(fishing));
|
||||
leaderboardMap.put(PrimarySkillType.ALCHEMY, List.copyOf(alchemy));
|
||||
leaderboardMap.put(PrimarySkillType.CROSSBOWS, List.copyOf(crossbows));
|
||||
leaderboardMap.put(PrimarySkillType.TRIDENTS, List.copyOf(tridents));
|
||||
leaderboardMap.put(PrimarySkillType.MACES, List.copyOf(maces));
|
||||
|
||||
return LeaderboardStatus.UPDATED;
|
||||
}
|
||||
@ -1280,7 +1259,7 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
int currentPos = 1;
|
||||
|
||||
for (PlayerStat stat : statsList) {
|
||||
if (stat.name.equalsIgnoreCase(playerName)) {
|
||||
if (stat.playerName().equalsIgnoreCase(playerName)) {
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
@ -1290,18 +1269,11 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int putStat(List<PlayerStat> statList, String playerName, int statValue) {
|
||||
private int putStat(TreeSet<PlayerStat> statList, String playerName, int statValue) {
|
||||
statList.add(new PlayerStat(playerName, statValue));
|
||||
return statValue;
|
||||
}
|
||||
|
||||
private static class SkillComparator implements Comparator<PlayerStat> {
|
||||
@Override
|
||||
public int compare(PlayerStat o1, PlayerStat o2) {
|
||||
return (o2.statVal - o1.statVal);
|
||||
}
|
||||
}
|
||||
|
||||
private PlayerProfile loadFromLine(@NotNull String[] character) {
|
||||
Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(character); // Skill levels
|
||||
Map<PrimarySkillType, Float> skillsXp = new EnumMap<>(
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
package com.gmail.nossr50.datatypes.database;
|
||||
|
||||
public class PlayerStat {
|
||||
public String name;
|
||||
public int statVal = 0;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public PlayerStat(String name, int value) {
|
||||
this.name = name;
|
||||
this.statVal = value;
|
||||
public record PlayerStat(String playerName, int value) implements Comparable<PlayerStat> {
|
||||
@Override
|
||||
public int compareTo(@NotNull PlayerStat o) {
|
||||
// Descending order
|
||||
int cmp = Integer.compare(o.value, this.value);
|
||||
if (cmp != 0) return cmp;
|
||||
// Tie-breaker
|
||||
return this.playerName.compareTo(o.playerName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,10 +79,11 @@ public class MctopCommandDisplayTask extends CancellableRunnable {
|
||||
// 12. Playername - skill value
|
||||
if (sender instanceof Player) {
|
||||
sender.sendMessage(
|
||||
String.format("%2d. %s%s - %s%s", place, ChatColor.GREEN, stat.name,
|
||||
ChatColor.WHITE, stat.statVal));
|
||||
String.format("%2d. %s%s - %s%s", place, ChatColor.GREEN, stat.playerName(),
|
||||
ChatColor.WHITE, stat.value()));
|
||||
} else {
|
||||
sender.sendMessage(String.format("%2d. %s - %s", place, stat.name, stat.statVal));
|
||||
sender.sendMessage(String.format("%2d. %s - %s", place, stat.playerName(),
|
||||
stat.value()));
|
||||
}
|
||||
|
||||
place++;
|
||||
|
||||
@ -43,6 +43,7 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.Damageable;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
public class WoodcuttingManager extends SkillManager {
|
||||
public static final String SAPLING = "sapling";
|
||||
@ -206,7 +207,8 @@ public class WoodcuttingManager extends SkillManager {
|
||||
* and 10-15 milliseconds on jungle trees once the JIT has optimized the function (use the
|
||||
* ability about 4 times before taking measurements).
|
||||
*/
|
||||
private void processTree(Block block, Set<Block> treeFellerBlocks) {
|
||||
@VisibleForTesting
|
||||
void processTree(Block block, Set<Block> treeFellerBlocks) {
|
||||
List<Block> futureCenterBlocks = new ArrayList<>();
|
||||
|
||||
// Check the block up and take different behavior (smaller search) if it's a log
|
||||
|
||||
@ -655,13 +655,13 @@ public class ScoreboardWrapper {
|
||||
|
||||
public void acceptLeaderboardData(@NotNull List<PlayerStat> leaderboardData) {
|
||||
for (PlayerStat stat : leaderboardData) {
|
||||
String name = stat.name;
|
||||
String name = stat.playerName();
|
||||
|
||||
if (name.equals(playerName)) {
|
||||
name = ChatColor.GOLD + "--You--";
|
||||
}
|
||||
|
||||
sidebarObjective.getScore(name).setScore(stat.statVal);
|
||||
sidebarObjective.getScore(name).setScore(stat.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,24 +1,40 @@
|
||||
package com.gmail.nossr50.skills.woodcutting;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
import com.gmail.nossr50.MMOTestEnvironment;
|
||||
import com.gmail.nossr50.api.exceptions.InvalidSkillException;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.BlockUtils;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
class WoodcuttingTest extends MMOTestEnvironment {
|
||||
@ -69,7 +85,7 @@ class WoodcuttingTest extends MMOTestEnvironment {
|
||||
void harvestLumberShouldDoubleDrop() {
|
||||
mmoPlayer.modifySkill(PrimarySkillType.WOODCUTTING, 1000);
|
||||
|
||||
Block block = Mockito.mock(Block.class);
|
||||
Block block = mock(Block.class);
|
||||
// return empty collection if ItemStack
|
||||
Mockito.when(block.getDrops(any())).thenReturn(Collections.emptyList());
|
||||
Mockito.when(block.getType()).thenReturn(Material.OAK_LOG);
|
||||
@ -86,7 +102,7 @@ class WoodcuttingTest extends MMOTestEnvironment {
|
||||
void harvestLumberShouldNotDoubleDrop() {
|
||||
mmoPlayer.modifySkill(PrimarySkillType.WOODCUTTING, 0);
|
||||
|
||||
Block block = Mockito.mock(Block.class);
|
||||
Block block = mock(Block.class);
|
||||
// wire block
|
||||
|
||||
Mockito.when(block.getDrops(any())).thenReturn(null);
|
||||
@ -99,7 +115,7 @@ class WoodcuttingTest extends MMOTestEnvironment {
|
||||
|
||||
@Test
|
||||
void testProcessWoodcuttingBlockXP() {
|
||||
Block targetBlock = Mockito.mock(Block.class);
|
||||
Block targetBlock = mock(Block.class);
|
||||
Mockito.when(targetBlock.getType()).thenReturn(Material.OAK_LOG);
|
||||
// wire XP
|
||||
Mockito.when(ExperienceConfig.getInstance()
|
||||
@ -110,4 +126,109 @@ class WoodcuttingTest extends MMOTestEnvironment {
|
||||
Mockito.verify(mmoPlayer, Mockito.times(1))
|
||||
.beginXpGain(eq(PrimarySkillType.WOODCUTTING), eq(5F), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void treeFellerShouldStopAtThreshold() {
|
||||
// Set threshold artificially low
|
||||
int fakeThreshold = 3;
|
||||
Mockito.when(generalConfig.getTreeFellerThreshold()).thenReturn(fakeThreshold);
|
||||
|
||||
WoodcuttingManager manager = Mockito.spy(new WoodcuttingManager(mmoPlayer));
|
||||
|
||||
// Simulate all blocks are logs with XP
|
||||
MockedStatic<BlockUtils> mockedBlockUtils = mockStatic(BlockUtils.class);
|
||||
mockedBlockUtils.when(() -> BlockUtils.hasWoodcuttingXP(any(Block.class))).thenReturn(true);
|
||||
mockedBlockUtils.when(() -> BlockUtils.isNonWoodPartOfTree(any(Block.class)))
|
||||
.thenReturn(false);
|
||||
|
||||
// Simulate that block tracker always allows processing
|
||||
Mockito.when(mcMMO.getUserBlockTracker().isIneligible(any(Block.class))).thenReturn(false);
|
||||
|
||||
// Create distinct mocked blocks to simulate recursion
|
||||
Block centerBlock = mock(Block.class);
|
||||
List<Block> relatives = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Block relative = mock(Block.class, "block_" + i);
|
||||
Mockito.when(relative.getRelative(any(BlockFace.class))).thenReturn(relative);
|
||||
Mockito.when(relative.getRelative(anyInt(), anyInt(), anyInt())).thenReturn(relative);
|
||||
relatives.add(relative);
|
||||
}
|
||||
|
||||
// Wire center block to return a different relative each time
|
||||
Mockito.when(centerBlock.getRelative(any(BlockFace.class)))
|
||||
.thenAnswer(inv -> relatives.get(0));
|
||||
Mockito.when(centerBlock.getRelative(anyInt(), anyInt(), anyInt()))
|
||||
.thenAnswer(inv -> relatives.get(
|
||||
ThreadLocalRandom.current().nextInt(relatives.size())));
|
||||
|
||||
Set<Block> treeFellerBlocks = new HashSet<>();
|
||||
manager.processTree(centerBlock, treeFellerBlocks);
|
||||
|
||||
// --- Assertions ---
|
||||
|
||||
// It processed *at least one* block
|
||||
assertFalse(treeFellerBlocks.isEmpty(), "Tree Feller should process at least one block");
|
||||
|
||||
// It reached or slightly exceeded the threshold
|
||||
assertTrue(treeFellerBlocks.size() >= fakeThreshold,
|
||||
"Tree Feller should process up to the threshold limit");
|
||||
|
||||
// Confirm it stopped due to the threshold
|
||||
assertTrue(getPrivateTreeFellerReachedThreshold(manager),
|
||||
"Tree Feller should set treeFellerReachedThreshold to true");
|
||||
|
||||
mockedBlockUtils.close();
|
||||
}
|
||||
|
||||
private boolean getPrivateTreeFellerReachedThreshold(WoodcuttingManager manager) {
|
||||
try {
|
||||
Field field = WoodcuttingManager.class.getDeclaredField("treeFellerReachedThreshold");
|
||||
field.setAccessible(true);
|
||||
return (boolean) field.get(manager);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void treeFellerShouldNotReachThreshold() throws NoSuchFieldException, IllegalAccessException {
|
||||
int threshold = 10;
|
||||
Mockito.when(generalConfig.getTreeFellerThreshold()).thenReturn(threshold);
|
||||
|
||||
WoodcuttingManager manager = Mockito.spy(new WoodcuttingManager(mmoPlayer));
|
||||
|
||||
MockedStatic<BlockUtils> mockedBlockUtils = mockStatic(BlockUtils.class);
|
||||
mockedBlockUtils.when(() -> BlockUtils.hasWoodcuttingXP(any(Block.class))).thenReturn(true);
|
||||
mockedBlockUtils.when(() -> BlockUtils.isNonWoodPartOfTree(any(Block.class)))
|
||||
.thenReturn(false);
|
||||
Mockito.when(mcMMO.getUserBlockTracker().isIneligible(any(Block.class))).thenReturn(false);
|
||||
|
||||
// Create 4 blocks (well below threshold)
|
||||
Block b0 = mock(Block.class, "b0");
|
||||
Block b1 = mock(Block.class, "b1");
|
||||
Block b2 = mock(Block.class, "b2");
|
||||
Block b3 = mock(Block.class, "b3");
|
||||
|
||||
// Deterministically chain recursion: b0 → b1 → b2 → b3 → null
|
||||
Mockito.when(b0.getRelative(anyInt(), anyInt(), anyInt())).thenReturn(b1);
|
||||
Mockito.when(b1.getRelative(anyInt(), anyInt(), anyInt())).thenReturn(b2);
|
||||
Mockito.when(b2.getRelative(anyInt(), anyInt(), anyInt())).thenReturn(b3);
|
||||
Mockito.when(b3.getRelative(anyInt(), anyInt(), anyInt())).thenReturn(null);
|
||||
|
||||
Mockito.when(b0.getRelative(any(BlockFace.class))).thenReturn(b1);
|
||||
Mockito.when(b1.getRelative(any(BlockFace.class))).thenReturn(b2);
|
||||
Mockito.when(b2.getRelative(any(BlockFace.class))).thenReturn(b3);
|
||||
Mockito.when(b3.getRelative(any(BlockFace.class))).thenReturn(null);
|
||||
|
||||
Set<Block> processed = new HashSet<>();
|
||||
manager.processTree(b0, processed);
|
||||
|
||||
assertEquals(3, processed.size(), "Should process exactly 4 blocks");
|
||||
assertFalse(getPrivateTreeFellerReachedThreshold(manager),
|
||||
"treeFellerReachedThreshold should remain false");
|
||||
|
||||
mockedBlockUtils.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user