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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ release.properties
dependency-reduced-pom.xml
buildNumber.properties

# Test artifacts
database/
database_backup/

# Intellij
*.iml
*.java___jb_tmp___
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/com/wasteofplastic/invswitcher/Store.java
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,15 @@ private void setFood(InventoryStorage store, Player player, String overworldName

private void setAdvancements(InventoryStorage store, Player player, String overworldName) {
// Advancements
store.getAdvancements(overworldName).forEach((k, v) -> {
Map<String, List<String>> advancements = store.getAdvancements(overworldName);
if (advancements.isEmpty()) {
return;
}
// Save current experience before granting advancements, because some advancements
// reward XP when their criteria are awarded, which would incorrectly increase the
// player's experience points.
int savedExp = getTotalExperience(player);
advancements.forEach((k, v) -> {
Iterator<Advancement> it = Bukkit.advancementIterator();
while (it.hasNext()) {
Advancement a = it.next();
Expand All @@ -330,7 +338,8 @@ private void setAdvancements(InventoryStorage store, Player player, String overw
}
}
});

// Restore experience to prevent advancement rewards from modifying it
setTotalExperience(player, savedExp);
}

public void removeFromCache(Player player) {
Expand Down
44 changes: 44 additions & 0 deletions src/test/java/com/wasteofplastic/invswitcher/StoreTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
Expand All @@ -30,8 +31,11 @@
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
Expand Down Expand Up @@ -200,6 +204,46 @@
verify(player).setTotalExperience(0);
}

/**
* Test that advancement grants during {@link Store#getInventory} do not modify the player's
* experience points. Some advancements reward XP when their criteria are awarded; the store
* must save and restore XP around the advancement grant step.
*/
@Test
public void testGetInventoryAdvancementsPreservesExperience() {

Check warning on line 213 in src/test/java/com/wasteofplastic/invswitcher/StoreTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this 'public' modifier.

See more on https://sonarcloud.io/project/issues?id=BentoBoxWorld_addon-invSwitcher&issues=AZ5Hd40FRiG7CBVeEoLl&open=AZ5Hd40FRiG7CBVeEoLl&pullRequest=47
sets.setAdvancements(true);
sets.setExperience(true);
sets.setStatistics(false);
sets.setIslandsActive(false);

// Mock an advancement with awarded criteria
Advancement advancement = mock(Advancement.class);
NamespacedKey advKey = NamespacedKey.minecraft("story_mine_stone");
when(advancement.getKey()).thenReturn(advKey);

AdvancementProgress progress = mock(AdvancementProgress.class);
Set<String> criteria = new HashSet<>(Set.of("mine_stone"));
when(progress.getAwardedCriteria()).thenReturn(criteria);
when(player.getAdvancementProgress(advancement)).thenReturn(progress);

try (MockedStatic<Bukkit> mockedBukkit = mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS)) {
// Return a fresh iterator each time so both storeInventory and getInventory can iterate
mockedBukkit.when(Bukkit::advancementIterator).thenAnswer(inv -> List.of(advancement).iterator());

// Store inventory (saves advancement data and clears player including XP reset)
s.storeInventory(player, world);

// Load inventory — experience set, advancements granted, XP restored
s.getInventory(player, world);
}

// setTotalExperience should be called exactly 3 times:
// 1. clearPlayer during storeInventory (XP reset to 0)
// 2. experience loading during getInventory (XP set to stored value 0)
// 3. XP restoration inside setAdvancements after granting advancement criteria
verify(player, times(3)).setTotalExperience(0);
}

/**
* Test method for {@link com.wasteofplastic.invswitcher.Store#removeFromCache(org.bukkit.entity.Player)}.
*/
Expand Down
Loading