Skip to content

Commit e892098

Browse files
committed
Fix xp quicktake/store orb spawn value race; gate xp debug logs
1 parent 85e8850 commit e892098

File tree

3 files changed

+76
-5
lines changed

3 files changed

+76
-5
lines changed

src/main/java/cat/nyaa/ukit/utils/ExperienceUtils.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import java.util.List;
1111
import java.util.Random;
12+
import java.util.function.Consumer;
1213

1314

1415
public final class ExperienceUtils {
@@ -78,13 +79,23 @@ public static double conditionalRounding(double value) {
7879
}
7980

8081
public static void splashExp(int amount, Location location) {
82+
splashExp(amount, location, null);
83+
}
84+
85+
public static void splashExp(int amount, Location location, Consumer<ExperienceOrb> orbCustomizer) {
8186
var nextOrbValueIndex = 0;
8287
while (amount > 0) {
8388
nextOrbValueIndex = firstMatchedExpIndex(amount, nextOrbValueIndex);
8489
var nextOrbValue = usableSplashExpList.get(nextOrbValueIndex);
85-
var experienceOrb = location.getWorld().spawn(location, ExperienceOrb.class, CreatureSpawnEvent.SpawnReason.CUSTOM);
86-
experienceOrb.setExperience(nextOrbValue);
87-
experienceOrb.setVelocity(randomVector().multiply(0.3));
90+
location.getWorld().spawn(location, ExperienceOrb.class, orb -> {
91+
// Configure orb value before it is added to world so merge-on-spawn
92+
// sees the final value instead of default 0.
93+
orb.setExperience(nextOrbValue);
94+
orb.setVelocity(randomVector().multiply(0.3));
95+
if (orbCustomizer != null) {
96+
orbCustomizer.accept(orb);
97+
}
98+
}, CreatureSpawnEvent.SpawnReason.CUSTOM);
8899
amount -= nextOrbValue;
89100
}
90101
}

src/main/java/cat/nyaa/ukit/xpstore/XpStoreConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ public class XpStoreConfig {
66
public int quickTakeMinimumAmount = 10;
77
public int quickTakeArmTimeInMillisecond = 2000;
88
public double quickTakeRatio = 0.01;
9+
public boolean debugLog = false;
10+
public boolean debugVerbose = false;
911
}

src/main/java/cat/nyaa/ukit/xpstore/XpStoreFunction.java

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class XpStoreFunction implements SubCommandExecutor, SubTabCompleter, Lis
3737
private final NamespacedKey QuickTakePreferenceKey;
3838
private final NamespacedKey QuickTakeLoreIndexKey;
3939
private final NamespacedKey ThrownExpAmountKey;
40+
private final NamespacedKey XpStoreOrbMarkerKey;
4041
private final String EXPBOTTLE_PERMISSION_NODE = "ukit.xpstore";
4142
private final Map<UUID, Long> quickTakeArmMap = new HashMap<>();
4243
private final List<String> subCommands = List.of("store", "take", "quicktake");
@@ -48,6 +49,7 @@ public XpStoreFunction(SpigotLoader pluginInstance) {
4849
QuickTakeLoreIndexKey = new NamespacedKey(pluginInstance, "QUICK_TAKE_LORE_INDEX");
4950
QuickTakePreferenceKey = new NamespacedKey(pluginInstance, "QUICK_TAKE_AMOUNT");
5051
ThrownExpAmountKey = new NamespacedKey(pluginInstance, "THROWN_EXP_AMOUNT");
52+
XpStoreOrbMarkerKey = new NamespacedKey(pluginInstance, "XPSTORE_ORB_MARKER");
5153
}
5254

5355
@Override
@@ -91,6 +93,13 @@ public boolean invokeCommand(CommandSender commandSender, Command command, Strin
9193
}
9294
if (args[0].equalsIgnoreCase("store")) {
9395
var expTotal = amountItem * amountInput;
96+
debugLog("store request player=" + senderPlayer.getName()
97+
+ " thread=" + Thread.currentThread().getName()
98+
+ " bottles=" + amountItem
99+
+ " amount_per_bottle=" + amountInput
100+
+ " total=" + expTotal
101+
+ " exp_in_item=" + expInItem
102+
+ " exp_player=" + ExperienceUtils.getExpPoints(senderPlayer));
94103
if (ExperienceUtils.getExpPoints(senderPlayer) < expTotal) {
95104
senderPlayer.sendMessage(pluginInstance.language.xpStoreLang.notEnoughExp.produce(
96105
Pair.of("expTotal", expTotal),
@@ -115,6 +124,13 @@ public boolean invokeCommand(CommandSender commandSender, Command command, Strin
115124
var expTotal = getMinimumDivisible(amountInput, amountItem);
116125
var amountAverage = expTotal / amountItem;
117126
var amountContained = getExpContained(itemInHand);
127+
debugLog("take request player=" + senderPlayer.getName()
128+
+ " thread=" + Thread.currentThread().getName()
129+
+ " bottles=" + amountItem
130+
+ " ask_total=" + amountInput
131+
+ " actual_total=" + expTotal
132+
+ " amount_per_bottle=" + amountAverage
133+
+ " amount_contained=" + amountContained);
118134
if (amountContained < amountAverage) {
119135
senderPlayer.sendMessage(pluginInstance.language.xpStoreLang.notEnoughExpInBottle.produce(
120136
Pair.of("amount", amountAverage)
@@ -285,6 +301,19 @@ private Integer getQuickTakePreference(ItemStack itemStack) {
285301
return meta.getPersistentDataContainer().get(QuickTakePreferenceKey, PersistentDataType.INTEGER);
286302
}
287303

304+
private boolean isDebugEnabled() {
305+
return pluginInstance.config.xpStoreConfig.debugLog;
306+
}
307+
308+
private boolean isDebugVerbose() {
309+
return pluginInstance.config.xpStoreConfig.debugVerbose;
310+
}
311+
312+
private void debugLog(String message) {
313+
if (!isDebugEnabled()) return;
314+
pluginInstance.getLogger().info("[xpstore-debug] " + message);
315+
}
316+
288317
@EventHandler(ignoreCancelled = true)
289318
public void onPlayerLaunchExpBottle(PlayerLaunchProjectileEvent event) {
290319
if (!(event.getProjectile() instanceof ThrownExpBottle thrownExpBottle)) return;
@@ -293,6 +322,12 @@ public void onPlayerLaunchExpBottle(PlayerLaunchProjectileEvent event) {
293322
int expAmount = getExpContained(item);
294323
if (expAmount <= 0) return;
295324
thrownExpBottle.getPersistentDataContainer().set(ThrownExpAmountKey, PersistentDataType.INTEGER, expAmount);
325+
if (isDebugVerbose()) {
326+
debugLog("launch(PlayerLaunchProjectileEvent) thread=" + Thread.currentThread().getName()
327+
+ " player=" + event.getPlayer().getName()
328+
+ " projectile=" + thrownExpBottle.getUniqueId()
329+
+ " exp_amount=" + expAmount);
330+
}
296331
}
297332

298333
@EventHandler(ignoreCancelled = true)
@@ -304,21 +339,36 @@ public void onExpBottleLaunch(ProjectileLaunchEvent event) {
304339
int expAmount = getExpContained(item);
305340
if (expAmount <= 0) return;
306341
thrownExpBottle.getPersistentDataContainer().set(ThrownExpAmountKey, PersistentDataType.INTEGER, expAmount);
342+
if (isDebugVerbose()) {
343+
debugLog("launch(ProjectileLaunchEvent) thread=" + Thread.currentThread().getName()
344+
+ " projectile=" + thrownExpBottle.getUniqueId()
345+
+ " exp_amount=" + expAmount);
346+
}
307347
}
308348

309349
@EventHandler(priority = EventPriority.HIGHEST)
310350
public void onExpBottleHit(ExpBottleEvent event) {
311351
ThrownExpBottle thrownExpBottle = event.getEntity();
352+
int vanillaExp = event.getExperience();
312353
Integer expAmount = thrownExpBottle.getPersistentDataContainer().get(ThrownExpAmountKey, PersistentDataType.INTEGER);
354+
boolean fromProjectilePdc = expAmount != null;
313355
if (expAmount == null) {
314356
var item = thrownExpBottle.getItem();
315357
if (!isExpContainer(item)) return;
316358
expAmount = getExpContained(item);
317359
}
318360
if (expAmount <= 0) return;
319361
event.setExperience(0);
320-
ExperienceUtils.splashExp(expAmount, thrownExpBottle.getLocation());
362+
ExperienceUtils.splashExp(expAmount, thrownExpBottle.getLocation(), orb ->
363+
orb.getPersistentDataContainer().set(XpStoreOrbMarkerKey, PersistentDataType.BYTE, (byte) 1)
364+
);
321365
thrownExpBottle.getPersistentDataContainer().remove(ThrownExpAmountKey);
366+
debugLog("hit(ExpBottleEvent) thread=" + Thread.currentThread().getName()
367+
+ " projectile=" + thrownExpBottle.getUniqueId()
368+
+ " vanilla_exp=" + vanillaExp
369+
+ " ukit_exp=" + expAmount
370+
+ " source=" + (fromProjectilePdc ? "projectile_pdc" : "item_pdc_fallback")
371+
+ " show_effect=" + event.getShowEffect());
322372
}
323373

324374
@EventHandler
@@ -351,7 +401,15 @@ public void onRightClickBottle(PlayerInteractEvent event) {
351401
addExpToItemStack(item, -amountTake);
352402
Utils.setItemInHand(event.getPlayer(), Pair.of(event.getHand(), item));
353403
var amountTotal = itemAmount * amountTake;
354-
ExperienceUtils.splashExp(amountTotal, event.getPlayer().getLocation());
404+
ExperienceUtils.splashExp(amountTotal, event.getPlayer().getLocation(), orb ->
405+
orb.getPersistentDataContainer().set(XpStoreOrbMarkerKey, PersistentDataType.BYTE, (byte) 1)
406+
);
407+
debugLog("quicktake produce thread=" + Thread.currentThread().getName()
408+
+ " player=" + event.getPlayer().getName()
409+
+ " item_amount=" + itemAmount
410+
+ " amount_take_per_bottle=" + amountTake
411+
+ " total_spawned=" + amountTotal
412+
+ " remaining_per_bottle=" + getExpContained(item));
355413

356414
event.getPlayer().sendActionBar(pluginInstance.language.xpStoreLang.quickTakeNotice.produce(
357415
Pair.of("amount", amountTotal),

0 commit comments

Comments
 (0)