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
30 changes: 30 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# InvSwitcher — .github/workflows/publish.yml
# Publishes the jar attached to a GitHub release to CurseForge and Hangar via the
# shared BentoBoxWorld/.github reusable workflow. Downloads the release asset instead of
# rebuilding from source. The reusable workflow is pinned to a commit SHA (Sonar
# githubactions:S7637). workflow_dispatch lets you (re)publish a given version.

name: Publish release to CurseForge and Hangar

on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: "Version to publish (e.g. 1.2.3)"
required: true
type: string

jobs:
publish:
uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@fe4b1f03c19f4fd4212020a06a07a7097923adec # master
with:
use_release_asset: "true" # publish the jar attached to the release; do not rebuild
hangar_slug: "InvSwitcher" # blank = skip Hangar
curseforge_id: "1515498"
game_versions: "26.2,26.1.2,26.1.1,26.1,1.21.11,1.21.10,1.21.9,1.21.8,1.21.7,1.21.6,1.21.5"
version: ${{ inputs.version }} # empty on release events -> falls back to the release tag
secrets:
HANGAR_API_KEY: ${{ secrets.HANGAR_API_KEY }}
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.19.0</build.version>
<build.version>1.19.1</build.version>
<!-- Sonar Cloud -->
<sonar.projectKey>BentoBoxWorld_addon-invSwitcher</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
Expand Down
24 changes: 15 additions & 9 deletions src/main/java/com/wasteofplastic/invswitcher/Store.java
Original file line number Diff line number Diff line change
Expand Up @@ -304,16 +304,22 @@ public void getInventory(Player player, World world, Island island) {
}

private void setHeath(InventoryStorage store, Player player, String overworldName) {
// Health
double health = store.getHealth().getOrDefault(overworldName,
player.getAttribute(Attribute.MAX_HEALTH).getValue());

AttributeInstance attr = player.getAttribute(Attribute.MAX_HEALTH);
if (attr != null && health > attr.getValue()) {
health = attr.getValue();
}
if (health < 0D) {
health = 0D;
double maxHealth = (attr != null) ? attr.getValue() : 20D;

// Health
double health = store.getHealth().getOrDefault(overworldName, maxHealth);

if (health > maxHealth) {
health = maxHealth;
}
// Never load a fatal health value. A stored value of 0 (or less) only arises when the
// player's state was captured mid-death (e.g. when they died on another island). Applying
// it to a live player would kill them again the moment they load the world, causing an
// endless respawn/death loop (issue #56). Restore full health instead, matching vanilla
// respawn behaviour.
if (health <= 0D) {
health = maxHealth;
}
player.setHealth(health);

Expand Down
23 changes: 23 additions & 0 deletions src/test/java/com/wasteofplastic/invswitcher/StoreTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,29 @@ void testGetInventory() {
verify(player).setTotalExperience(0);
}

/**
* Issue #56: a stored health of 0 is only ever captured when the player's state is saved
* mid-death (e.g. per-island health enabled and the player died on another island). Loading
* that value into a live player would kill them the instant they enter the world, causing an
* endless respawn/death loop. Loading must restore full health instead of applying 0.
*/
@Test
void testGetInventoryNeverLoadsFatalHealth() {
sets.setStatistics(false);
sets.setAdvancements(false);

// Simulate the player's state being captured mid-death with 0 health
when(player.getHealth()).thenReturn(0D);
try (MockedStatic<Bukkit> mockedBukkit = mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS)) {
s.storeInventory(player, world);
}

// Re-entering the world must restore full health (max = 18), never the fatal stored 0
s.getInventory(player, world);
verify(player).setHealth(18D);
verify(player, never()).setHealth(0D);
}

/**
* 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
Expand Down
Loading