Skip to content

Commit 1bc754d

Browse files
authored
Merge pull request #359 from BentoBoxWorld/358_more_accurate_zero
More accurate island zeroing
2 parents 59fa018 + 791cb06 commit 1bc754d

File tree

10 files changed

+351
-206
lines changed

10 files changed

+351
-206
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
<!-- Do not change unless you want different name for local builds. -->
6868
<build.number>-LOCAL</build.number>
6969
<!-- This allows to change between versions. -->
70-
<build.version>2.19.3</build.version>
70+
<build.version>2.20.0</build.version>
7171
<sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey>
7272
<sonar.organization>bentobox-world</sonar.organization>
7373
<sonar.host.url>https://sonarcloud.io</sonar.host.url>

src/main/java/world/bentobox/level/Level.java

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,13 @@ public long getIslandLevel(World world, @Nullable UUID targetPlayer) {
339339
}
340340

341341
/**
342-
* Sets the player's level to a value
343-
*
344-
* @param world - world
345-
* @param targetPlayer - target player
346-
* @param level - level
347-
*/
342+
* Sets the player's level to a value. This will only last until the player reruns the level command
343+
*
344+
* @param world - world
345+
* @param targetPlayer - target player
346+
* @param level - level
347+
* @deprecated This is a useless method.
348+
*/
348349
public void setIslandLevel(World world, UUID targetPlayer, long level) {
349350
getManager().setIslandLevel(world, targetPlayer, level);
350351
}
@@ -355,19 +356,31 @@ public void setIslandLevel(World world, UUID targetPlayer, long level) {
355356
* @param island - island
356357
* @param level - initial calculated island level
357358
*/
358-
public void setInitialIslandLevel(@NonNull Island island, long level) {
359-
getManager().setInitialIslandLevel(island, level);
360-
}
359+
/* TODO
360+
public void setInitialIslandLevel(@NonNull Island island, long level) {
361+
getManager().setInitialIslandLevel(island, level);
362+
}
363+
364+
/**
365+
* Get the initial island level
366+
*
367+
* @param island - island
368+
* @return level or 0 by default
369+
*/
370+
/* TODO
371+
public long getInitialIslandLevel(@NonNull Island island) {
372+
return getManager().getInitialLevel(island);
373+
}*/
361374

362-
/**
363-
* Get the initial island level
364-
*
365-
* @param island - island
366-
* @return level or 0 by default
367-
*/
368-
public long getInitialIslandLevel(@NonNull Island island) {
369-
return getManager().getInitialLevel(island);
370-
}
375+
/**
376+
* Get the initial island count
377+
*
378+
* @param island - island
379+
* @return count or 0 by default
380+
*/
381+
public long getInitialIslandCount(@NonNull Island island) {
382+
return getManager().getInitialCount(island);
383+
}
371384

372385
/**
373386
* Calculates a user's island
@@ -392,21 +405,22 @@ public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID
392405
* @return LevelsData object or null if not found. Only island levels are set!
393406
* @deprecated Do not use this anymore. Use {@link #getIslandLevel(World, UUID)}
394407
*/
395-
@Deprecated(since = "2.3.0", forRemoval = true)
396-
public LevelsData getLevelsData(UUID targetPlayer) {
397-
LevelsData ld = new LevelsData(targetPlayer);
398-
getPlugin().getAddonsManager().getGameModeAddons().stream()
399-
.filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
400-
if (getSettings().isZeroNewIslandLevels()) {
401-
Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer);
402-
if (island != null) {
403-
ld.setInitialLevel(gm.getOverWorld(), this.getInitialIslandLevel(island));
404-
}
405-
}
406-
ld.setLevel(gm.getOverWorld(), this.getIslandLevel(gm.getOverWorld(), targetPlayer));
407-
});
408-
return ld;
409-
}
408+
/*
409+
@Deprecated(since = "2.3.0", forRemoval = true)
410+
public LevelsData getLevelsData(UUID targetPlayer) {
411+
LevelsData ld = new LevelsData(targetPlayer);
412+
getPlugin().getAddonsManager().getGameModeAddons().stream()
413+
.filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
414+
if (getSettings().isZeroNewIslandLevels()) {
415+
Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer);
416+
if (island != null) {
417+
ld.setInitialLevel(gm.getOverWorld(), this.getInitialIslandLevel(island));
418+
}
419+
}
420+
ld.setLevel(gm.getOverWorld(), this.getIslandLevel(gm.getOverWorld(), targetPlayer));
421+
});
422+
return ld;
423+
}*/
410424

411425
/**
412426
* @return the registeredGameModes

src/main/java/world/bentobox/level/LevelsManager.java

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package world.bentobox.level;
22

3+
import java.io.IOException;
34
import java.math.BigInteger;
45
import java.text.DecimalFormat;
6+
import java.text.ParseException;
57
import java.time.Instant;
68
import java.util.AbstractMap;
79
import java.util.Collections;
@@ -26,6 +28,7 @@
2628

2729
import world.bentobox.bentobox.database.Database;
2830
import world.bentobox.bentobox.database.objects.Island;
31+
import world.bentobox.level.calculators.EquationEvaluator;
2932
import world.bentobox.level.calculators.Results;
3033
import world.bentobox.level.events.IslandLevelCalculatedEvent;
3134
import world.bentobox.level.events.IslandPreLevelEvent;
@@ -130,7 +133,7 @@ private boolean fireIslandLevelCalcEvent(UUID targetPlayer, Island island, Resul
130133
return true;
131134
// Set the values if they were altered
132135
results.setLevel((Long) ilce.getKeyValues().getOrDefault("level", results.getLevel()));
133-
results.setInitialLevel((Long) ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel()));
136+
results.setInitialCount((Long) ilce.getKeyValues().getOrDefault("initialCount", results.getInitialCount()));
134137
results.setDeathHandicap((int) ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap()));
135138
results.setPointsToNextLevel(
136139
(Long) ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel()));
@@ -168,13 +171,65 @@ public String formatLevel(@Nullable Long lvl) {
168171
}
169172

170173
/**
171-
* Get the initial level of the island. Used to zero island levels
174+
* Get the initial count of the island. Used to zero island levels
172175
*
173176
* @param island - island
174-
* @return initial level of island
177+
* @return initial count of island
175178
*/
176-
public long getInitialLevel(Island island) {
177-
return getLevelsData(island).getInitialLevel();
179+
@SuppressWarnings("deprecation")
180+
public long getInitialCount(Island island) {
181+
Long initialLevel = getLevelsData(island).getInitialLevel(); // Backward compatibility check. For all new islands, this should be null.
182+
Long initialCount = getLevelsData(island).getInitialCount();
183+
if (initialLevel != null) {
184+
// Initial level exists so convert it
185+
if (initialCount == null) { // If initialCount is not null, then this is an edge case and initialCount will be used and initialLevel discarded
186+
// Convert from level to count
187+
initialCount = 0L;
188+
try {
189+
initialCount = getNumBlocks(initialLevel);
190+
} catch (Exception e) {
191+
addon.logError("Could not convert legacy initial level to count, so it will be set to 0. Error is: "
192+
+ e.getLocalizedMessage());
193+
initialCount = 0L;
194+
}
195+
}
196+
// Null out the old initial level and save
197+
getLevelsData(island).setInitialLevel(null);
198+
// Save
199+
this.setInitialIslandCount(island, initialCount);
200+
}
201+
// If initialCount doesn't exist, set it to 0L
202+
if (initialCount == null) {
203+
initialCount = 0L;
204+
getLevelsData(island).setInitialCount(0L);
205+
}
206+
return initialCount;
207+
}
208+
209+
/**
210+
* Runs the level calculation using the current formula until the level matches the initial value, or fails.
211+
* @param initialLevel - the old initial level
212+
* @return block count to obtain this level now
213+
* @throws ParseException if the formula for level calc is bugged
214+
* @throws IOException if the number of blocks cannot be found for this level
215+
*/
216+
private long getNumBlocks(final long initialLevel) throws ParseException, IOException {
217+
String calcString = addon.getSettings().getLevelCalc();
218+
int result = -1;
219+
long calculatedLevel = 0;
220+
String withCost = calcString.replace("level_cost", String.valueOf(this.addon.getSettings().getLevelCost()));
221+
long time = System.currentTimeMillis() + 10 * 1000; // 10 seconds
222+
do {
223+
result++;
224+
if (System.currentTimeMillis() > time) {
225+
throw new IOException("Timeout: Blocks cannot be found to create this initial level");
226+
}
227+
// Paste in the values to the formula
228+
String withValues = withCost.replace("blocks", String.valueOf(result));
229+
// Try and evaluate it
230+
calculatedLevel = (long) EquationEvaluator.eval(withValues);
231+
} while (calculatedLevel != initialLevel);
232+
return result;
178233
}
179234

180235
/**
@@ -422,15 +477,13 @@ public void removeEntry(World world, String uuid) {
422477
}
423478

424479
/**
425-
* Set an initial island level
480+
* Set an initial island count
426481
*
427-
* @param island - the island to set. Must have a non-null world
428-
* @param lv - initial island level
482+
* @param island - the island to set.
483+
* @param lv - initial island count
429484
*/
430-
public void setInitialIslandLevel(@NonNull Island island, long lv) {
431-
if (island.getWorld() == null)
432-
return;
433-
levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv);
485+
public void setInitialIslandCount(@NonNull Island island, long lv) {
486+
levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialCount(lv);
434487
handler.saveObjectAsync(levelsCache.get(island.getUniqueId()));
435488
}
436489

@@ -448,12 +501,7 @@ public void setIslandLevel(@NonNull World world, @NonNull UUID targetPlayer, lon
448501
if (island != null) {
449502
String id = island.getUniqueId();
450503
IslandLevels il = levelsCache.computeIfAbsent(id, IslandLevels::new);
451-
// Remove the initial level
452-
if (addon.getSettings().isZeroNewIslandLevels()) {
453-
il.setLevel(lv - il.getInitialLevel());
454-
} else {
455-
il.setLevel(lv);
456-
}
504+
il.setLevel(lv);
457505
handler.saveObjectAsync(levelsCache.get(id));
458506
// Update TopTen
459507
addToTopTen(island, levelsCache.get(id).getLevel());

src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ public IslandLevelCalculator(Level addon, Island island, CompletableFuture<Resul
9393
chunksToCheck = getChunksToScan(island);
9494
this.limitCount = new HashMap<>();
9595
// Get the initial island level
96-
results.initialLevel.set(addon.getInitialIslandLevel(island));
96+
// TODO: results.initialLevel.set(addon.getInitialIslandLevel(island));
97+
results.setInitialCount(addon.getInitialIslandCount(island));
9798
// Set up the worlds
9899
worlds.put(Environment.NORMAL, Util.getWorld(island.getWorld()));
99100
// Nether
@@ -117,19 +118,22 @@ public IslandLevelCalculator(Level addon, Island island, CompletableFuture<Resul
117118
/**
118119
* Calculate the level based on the raw points
119120
*
120-
* @param blockAndDeathPoints - raw points counted on island
121+
* @param rawPoints - raw points counted on island
121122
* @return level of island
122123
*/
123-
private long calculateLevel(long blockAndDeathPoints) {
124+
private long calculateLevel(final long rawPoints) {
124125
String calcString = addon.getSettings().getLevelCalc();
125-
String withValues = calcString.replace("blocks", String.valueOf(blockAndDeathPoints)).replace("level_cost",
126+
// Reduce count by initial count, if zeroing is done
127+
long modifiedPoints = rawPoints
128+
- (addon.getSettings().isZeroNewIslandLevels() ? results.initialCount.get() : 0);
129+
// Paste in the values to the formula
130+
String withValues = calcString.replace("blocks", String.valueOf(modifiedPoints)).replace("level_cost",
126131
String.valueOf(this.addon.getSettings().getLevelCost()));
127-
long evalWithValues;
132+
// Try and evaluate it
128133
try {
129-
evalWithValues = (long) EquationEvaluator.eval(withValues);
130-
return evalWithValues - (addon.getSettings().isZeroNewIslandLevels() ? results.initialLevel.get() : 0);
131-
134+
return (long) EquationEvaluator.eval(withValues);
132135
} catch (ParseException e) {
136+
// Hmm, error.
133137
addon.getPlugin().logStacktrace(e);
134138
return 0L;
135139
}
@@ -240,8 +244,12 @@ private List<String> getReport() {
240244
reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
241245
reportLines.add("Level cost = " + addon.getSettings().getLevelCost());
242246
reportLines.add("Deaths handicap = " + results.deathHandicap.get());
247+
/*
243248
if (addon.getSettings().isZeroNewIslandLevels()) {
244249
reportLines.add("Initial island level = " + (0L - addon.getManager().getInitialLevel(island)));
250+
}*/
251+
if (addon.getSettings().isZeroNewIslandLevels()) {
252+
reportLines.add("Initial island count = " + (0L - addon.getManager().getInitialCount(island)));
245253
}
246254
reportLines.add("Previous level = " + addon.getManager().getIslandLevel(island.getWorld(), island.getOwner()));
247255
reportLines.add("New level = " + results.getLevel());

src/main/java/world/bentobox/level/calculators/Results.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import com.google.common.collect.HashMultiset;
88
import com.google.common.collect.Multiset;
99

10+
/**
11+
* Where results are stored
12+
*/
1013
public class Results {
1114
public enum Result {
1215
/**
@@ -23,17 +26,33 @@ public enum Result {
2326
TIMEOUT
2427
}
2528
List<String> report;
29+
/**
30+
* MaterialData count anything above sea level
31+
*/
2632
final Multiset<Object> mdCount = HashMultiset.create();
33+
/**
34+
* Underwater count
35+
*/
2736
final Multiset<Object> uwCount = HashMultiset.create();
37+
/**
38+
* Not-in-config count - blocks not listed in the scoring config file
39+
*/
2840
final Multiset<Object> ncCount = HashMultiset.create();
41+
/**
42+
* Blocks not counted because they exceeded limits
43+
*/
2944
final Multiset<Object> ofCount = HashMultiset.create();
3045
// AtomicLong and AtomicInteger must be used because they are changed by multiple concurrent threads
3146
AtomicLong rawBlockCount = new AtomicLong(0);
3247
AtomicLong underWaterBlockCount = new AtomicLong(0);
3348
AtomicLong level = new AtomicLong(0);
3449
AtomicInteger deathHandicap = new AtomicInteger(0);
3550
AtomicLong pointsToNextLevel = new AtomicLong(0);
36-
AtomicLong initialLevel = new AtomicLong(0);
51+
//AtomicLong initialLevel = new AtomicLong(0);
52+
AtomicLong initialCount = new AtomicLong(0);
53+
/**
54+
* Total points before any death penalties
55+
*/
3756
AtomicLong totalPoints = new AtomicLong(0);
3857
final Result state;
3958

@@ -107,24 +126,26 @@ public void setTotalPoints(long points) {
107126
totalPoints.set(points);
108127
}
109128

129+
/*
110130
public long getInitialLevel() {
111131
return initialLevel.get();
112132
}
113-
133+
114134
public void setInitialLevel(long initialLevel) {
115135
this.initialLevel.set(initialLevel);
116136
}
117-
137+
*/
118138
/* (non-Javadoc)
119139
* @see java.lang.Object#toString()
120140
*/
141+
/*
121142
@Override
122143
public String toString() {
123144
return "Results [report=" + report + ", mdCount=" + mdCount + ", uwCount=" + uwCount + ", ncCount="
124145
+ ncCount + ", ofCount=" + ofCount + ", rawBlockCount=" + rawBlockCount + ", underWaterBlockCount="
125146
+ underWaterBlockCount + ", level=" + level + ", deathHandicap=" + deathHandicap
126147
+ ", pointsToNextLevel=" + pointsToNextLevel + ", totalPoints=" + totalPoints + ", initialLevel=" + initialLevel + "]";
127-
}
148+
}*/
128149
/**
129150
* @return the mdCount
130151
*/
@@ -144,4 +165,18 @@ public Result getState() {
144165
return state;
145166
}
146167

168+
/**
169+
* @return the initialCount
170+
*/
171+
public long getInitialCount() {
172+
return initialCount.get();
173+
}
174+
175+
/**
176+
* @param long1 the initialCount to set
177+
*/
178+
public void setInitialCount(Long count) {
179+
this.initialCount.set(count);
180+
}
181+
147182
}

0 commit comments

Comments
 (0)