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
16 changes: 5 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
<mock-bukkit.version>4.110.0</mock-bukkit.version>
<!-- More visible way how to change dependency versions -->
<bentobox.version>3.22.0</bentobox.version>
<items-adder.version>4.0.10</items-adder.version>
<items-adder.version>4.0.17</items-adder.version>
<nexo.version>1.8.0</nexo.version>
<craftengine.version>0.0.67</craftengine.version>
<level.version>2.6.2</level.version>
Expand All @@ -67,7 +67,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.27.1</build.version>
<build.version>1.28.0</build.version>
<!-- SonarCloud -->
<sonar.projectKey>BentoBoxWorld_AOneBlock</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
Expand Down Expand Up @@ -147,13 +147,6 @@
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
<!-- ItemsAdder API -->
<repository>
<id>matteodev</id>
<url>https://maven.devs.beer/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository>
<!-- Nexo API -->
<repository>
<id>nexomc</id>
Expand Down Expand Up @@ -269,8 +262,9 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.lone</groupId>
<artifactId>api-itemsadder</artifactId>
<!-- Published to Maven Central -->
<groupId>beer.devs</groupId>
<artifactId>itemsadder-api</artifactId>
<version>${items-adder.version}</version>
<scope>provided</scope>
</dependency>
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/world/bentobox/aoneblock/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -616,18 +616,24 @@ public class Settings implements WorldSettings {

// Deaths
@ConfigComment("Whether deaths are counted or not.")
@ConfigComment("If false, BentoBox does not count deaths and the Level addon does not record deaths against islands.")
@ConfigEntry(path = "island.deaths.counted")
private boolean deathsCounted = true;

@ConfigComment("Maximum number of deaths to count. The death count can be used by add-ons.")
@ConfigComment("Since Level 2.29.0 this also caps how many deaths each member can contribute to an island's death penalty.")
@ConfigEntry(path = "island.deaths.max")
private int deathsMax = 10;

@ConfigComment("When a player joins a team, reset their death count")
@ConfigComment("When a player joins a team, reset their death count.")
@ConfigComment("This only affects BentoBox's own per-player death count, used by the %aoneblock_deaths% placeholder.")
@ConfigComment("Since Level 2.29.0, island levels use per-island death tracking and are not affected by this setting.")
@ConfigEntry(path = "island.deaths.team-join-reset")
private boolean teamJoinDeathReset = true;

@ConfigComment("Reset player death count when they start a new island or reset an island")
@ConfigComment("Reset player death count when they start a new island or reset an island.")
@ConfigComment("This only affects BentoBox's own per-player death count.")
@ConfigComment("Since Level 2.29.0 the Level addon clears an island's own death record automatically when it is reset or deleted.")
@ConfigEntry(path = "island.deaths.reset-on-new-island") // , since = "1.6.0")
private boolean deathsResetOnNewIsland = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ private void process(@NonNull Cancellable e, @NonNull Island island, @Nullable P

// Process phase changes and requirements
ProcessPhaseResult phaseResult = processPhase(e, island, is, player, world, block);
if (e.isCancelled()) {
if (phaseResult == null || e.isCancelled()) {
return;
}

Expand Down Expand Up @@ -455,16 +455,29 @@ private record ProcessPhaseResult(OneBlockPhase phase, boolean isCurrPhaseNew, i
* @param player - player involved
* @param world - world where processing occurs
* @param block - block being processed
* @return ProcessPhaseResult containing phase details
* @return ProcessPhaseResult containing phase details, or null if no phase covers the
* island's block number. In that case the event is cancelled so the magic block
* is not lost, and an error is logged for the admin.
*/
@Nullable
private ProcessPhaseResult processPhase(Cancellable e, Island i, OneBlockIslands is, Player player, World world, Block block) {
OneBlockPhase phase = oneBlocksManager.getPhase(is.getBlockNumber());
String prevPhaseName = is.getPhaseName();

if (Objects.requireNonNull(phase).getGotoBlock() != null) {
if (phase != null && phase.getGotoBlock() != null) {
phase = handleGoto(is, phase.getGotoBlock());
}

if (phase == null) {
// No phase covers this block number. This happens when no phase files loaded,
// when the first phase does not start at block 0, or when a goto points below
// the first phase. Keep the block in place rather than breaking the island.
addon.logError("No phase found for block number " + is.getBlockNumber() + " on island "
+ i.getUniqueId() + ". Check the phase files in the phases folder.");
e.setCancelled(true);
return null;
}

String currPhaseName = phase.getPhaseName() == null ? "" : phase.getPhaseName();
handlePhaseChange(is, currPhaseName);

Expand Down Expand Up @@ -516,7 +529,7 @@ private void handleNewPhase(Player player, Island i, OneBlockIslands is, OneBloc
*/
private void handlePhaseChange(OneBlockIslands is, String currPhaseName) {
OneBlockPhase nextPhase = oneBlocksManager.getPhase(is.getBlockNumber() + 1);
if (Objects.requireNonNull(nextPhase).getGotoBlock() != null) {
if (nextPhase != null && nextPhase.getGotoBlock() != null) {
nextPhase = oneBlocksManager.getPhase(nextPhase.getGotoBlock());
}
String nextPhaseName = nextPhase == null || nextPhase.getPhaseName() == null ? "" : nextPhase.getPhaseName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -95,6 +96,8 @@ public class OneBlocksManager {
private static final String INDEX_PHASES = "phases";
private static final String GOTO_AT_END = "gotoAtEnd";
private static final String ADMIN_LENGTHS = "adminLengths";
private static final String SHIPPED_PHASES = "shippedPhases";
private static final String ADDED_IN = "addedIn";
private static final String CHESTS_YML_SUFFIX = "_chests.yml";
private static final String WEIGHT = "weight";
/**
Expand All @@ -115,6 +118,17 @@ public class OneBlocksManager {
* then never overwrites lengths from the files' legacy start-block keys.
*/
private boolean adminLengths;
/**
* Files of the phases shipped in the addon jar that this server has already
* been offered. A shipped phase not in this set is new in this addon version
* and is added to the index; one in it but missing from the index was
* removed by an admin and stays out.
*/
private Set<String> shippedPhases = new LinkedHashSet<>();
/**
* False when the index predates {@link #shippedPhases} being recorded.
*/
private boolean shippedPhasesRecorded;

/**
* @param addon - addon
Expand All @@ -136,6 +150,8 @@ public void loadPhases() throws IOException {
phaseIndex = new ArrayList<>();
gotoAtEnd = null;
adminLengths = false;
shippedPhases = new LinkedHashSet<>();
shippedPhasesRecorded = false;
// Check for folder
File check = new File(addon.getDataFolder(), PHASES);
if (check.mkdirs()) {
Expand Down Expand Up @@ -200,7 +216,8 @@ private boolean loadUsingIndex(File check) {
if (entries == null) {
return false;
}
boolean changed = reconcileIndex(entries, check, freshIndex);
boolean changed = addNewShippedPhases(entries);
changed |= reconcileIndex(entries, check, freshIndex);
if (entries.isEmpty()) {
return false;
}
Expand Down Expand Up @@ -257,9 +274,79 @@ private List<PhaseIndexEntry> readIndex(File indexFile) {
}
gotoAtEnd = index.contains(GOTO_AT_END) ? index.getInt(GOTO_AT_END, 0) : null;
adminLengths = index.getBoolean(ADMIN_LENGTHS, false);
shippedPhasesRecorded = index.contains(SHIPPED_PHASES);
shippedPhases = new LinkedHashSet<>(index.getStringList(SHIPPED_PHASES));
return entries;
}

/**
* Adds phases that are new in this addon version to an existing index. An
* index belongs to the server, so an addon upgrade never replaces it - without
* this, phases shipped by later versions would never appear on servers that
* already have an index.
* <p>
* A shipped phase is added when the index has no phase with its file or name
* and it is not in {@link #shippedPhases}, so phases an admin removed are
* never brought back. Indexes written before {@link #shippedPhases} existed
* treat every shipped phase without an {@code addedIn} tag as already offered.
* A new phase goes after the nearest shipped phase above it that the index
* has, and its files are copied from the jar.
*
* @param entries index entries, updated in place
* @return true if the entries or the offered phases changed
*/
boolean addNewShippedPhases(List<PhaseIndexEntry> entries) {
boolean changed = false;
String previous = null;
for (Map<?, ?> map : readShippedIndex()) {
PhaseIndexEntry shipped = PhaseIndexEntry.fromMap(map);
if (shipped == null) {
continue;
}
String file = shipped.getFile();
boolean offered = shippedPhases.contains(file) || (!shippedPhasesRecorded && !map.containsKey(ADDED_IN));
boolean inIndex = entries.stream().anyMatch(en -> en.getFile().equals(file)
|| (en.getName() != null && en.getName().equalsIgnoreCase(shipped.getName())));
if (!inIndex && !offered) {
entries.add(positionAfter(entries, previous), shipped);
restorePhaseFileFromJar(file);
addon.log("Phase index: added new phase " + shipped.getName()
+ " from this version of the addon. Move or disable it with /oba phases.");
inIndex = true;
}
if (inIndex) {
previous = file;
}
changed |= shippedPhases.add(file);
}
return changed;
}

/**
* @return the phase entries of the index shipped in the addon jar, or an
* empty list if the jar has none
*/
private List<Map<?, ?>> readShippedIndex() {
try {
return addon.getYamlFromJar(PHASES_INDEX_YML).getMapList(INDEX_PHASES);
} catch (Exception e) {
return List.of();
}
}

/**
* @return the position just after the entry for this file, or 0 if there is
* no such entry
*/
private int positionAfter(List<PhaseIndexEntry> entries, @Nullable String file) {
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getFile().equals(file)) {
return i + 1;
}
}
return 0;
}

/**
* Result of scanning the main phase files in the phases folder: every phase
* section with a numeric (legacy start-block) key, phase sections with any
Expand Down Expand Up @@ -639,6 +726,9 @@ private boolean writeIndex(File indexFile, List<PhaseIndexEntry> entries, @Nulla
if (adminLengths) {
index.set(ADMIN_LENGTHS, true);
}
if (!shippedPhases.isEmpty()) {
index.set(SHIPPED_PHASES, new ArrayList<>(shippedPhases));
}
try {
index.save(indexFile);
return true;
Expand Down
10 changes: 8 additions & 2 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -560,12 +560,18 @@ island:
require-confirmation: true
deaths:
# Whether deaths are counted or not.
# If false, BentoBox does not count deaths and the Level addon does not record deaths against islands.
counted: true
# Maximum number of deaths to count. The death count can be used by add-ons.
# Since Level 2.29.0 this also caps how many deaths each member can contribute to an island's death penalty.
max: 10
# When a player joins a team, reset their death count
# When a player joins a team, reset their death count.
# This only affects BentoBox's own per-player death count, used by the %aoneblock_deaths% placeholder.
# Since Level 2.29.0, island levels use per-island death tracking and are not affected by this setting.
team-join-reset: true
# Reset player death count when they start a new island or reset an island
# Reset player death count when they start a new island or reset an island.
# This only affects BentoBox's own per-player death count.
# Since Level 2.29.0 the Level addon clears an island's own death record automatically when it is reset or deleted.
reset-on-new-island: true
protection:
# Geo restrict mobs.
Expand Down
67 changes: 67 additions & 0 deletions src/main/resources/phases/15500_dappled_forest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Dappled Forest phase. The dappled_forest biome, poplar wood, poplar leaves
# and red shrub only exist from Minecraft 26.3, so this phase declares a
# required version and is skipped on older servers.
'15500':
name: Dappled Forest
icon: YELLOW_POPLAR_LEAVES
firstBlock: POPLAR_LOG
biome: DAPPLED_FOREST
requiredMinecraftVersion: '26.3'
fixedBlocks:
'0': POPLAR_LOG
'1': YELLOW_POPLAR_LEAVES
'2': ORANGE_POPLAR_LEAVES
'3': RED_POPLAR_LEAVES
'4': POPLAR_SAPLING
'5': RED_SHRUB
blocks:
POPLAR_LOG: 300
YELLOW_POPLAR_LEAVES: 80
ORANGE_POPLAR_LEAVES: 80
RED_POPLAR_LEAVES: 80
LEAF_LITTER: 80
RED_SHRUB: 40
GRASS_BLOCK: 100
DIRT: 100
COARSE_DIRT: 30
STONE: 100
BROWN_MUSHROOM_BLOCK: 20
MOSSY_COBBLESTONE: 20
# Wool stairs in every colour, so each one is a random colour
WHITE_WOOL_STAIRS: 5
LIGHT_GRAY_WOOL_STAIRS: 5
GRAY_WOOL_STAIRS: 5
BLACK_WOOL_STAIRS: 5
BROWN_WOOL_STAIRS: 5
RED_WOOL_STAIRS: 5
ORANGE_WOOL_STAIRS: 5
YELLOW_WOOL_STAIRS: 5
LIME_WOOL_STAIRS: 5
GREEN_WOOL_STAIRS: 5
CYAN_WOOL_STAIRS: 5
LIGHT_BLUE_WOOL_STAIRS: 5
BLUE_WOOL_STAIRS: 5
PURPLE_WOOL_STAIRS: 5
MAGENTA_WOOL_STAIRS: 5
PINK_WOOL_STAIRS: 5
CHEST: 60
mobs:
SHEEP: 60
PIG: 50
CHICKEN: 50
COW: 40
RABBIT: 20
FOX: 20
SPIDER: 50
SKELETON: 50
CREEPER: 50
SLIME: 50
ZOMBIE: 48
ENDERMAN: 5
BAT: 5
WITCH: 3
ZOMBIE_VILLAGER: 3
holograms:
'0': '&6Autumn has come to the forest!'
start-commands: []
end-commands: []
Loading
Loading