mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2026-08-18 03:05:10 +00:00
refactor: replace Rupture's Bukkit metadata with an owned registry (#5332)
This commit is contained in:
@ -2,7 +2,7 @@ package com.gmail.nossr50.api;
|
||||
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
|
||||
import com.gmail.nossr50.util.MetadataConstants;
|
||||
import com.gmail.nossr50.runnables.skills.RuptureTask;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -110,10 +110,6 @@ public final class AbilityAPI {
|
||||
}
|
||||
|
||||
public static boolean isBleeding(LivingEntity entity) {
|
||||
if (entity.isValid()) {
|
||||
return entity.hasMetadata(MetadataConstants.METADATA_KEY_RUPTURE);
|
||||
}
|
||||
|
||||
return false;
|
||||
return entity.isValid() && RuptureTask.getActive(entity) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,22 @@
|
||||
package com.gmail.nossr50.datatypes.meta;
|
||||
|
||||
import com.gmail.nossr50.api.AbilityAPI;
|
||||
import com.gmail.nossr50.runnables.skills.RuptureTask;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* No longer used. mcMMO stopped attaching rupture state to entities as Bukkit metadata
|
||||
* in 2.3.001; active ruptures are tracked internally instead. Use
|
||||
* {@link AbilityAPI#isBleeding(LivingEntity)} to check for an active bleed, or
|
||||
* {@link RuptureTask#getActive(Entity)} to retrieve the task.
|
||||
*
|
||||
* @deprecated mcMMO no longer creates or reads this metadata.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2.3.001")
|
||||
public class RuptureTaskMeta extends FixedMetadataValue {
|
||||
|
||||
private final @NotNull RuptureTask ruptureTask;
|
||||
|
||||
@ -12,7 +12,6 @@ import com.gmail.nossr50.datatypes.chat.ChatChannel;
|
||||
import com.gmail.nossr50.datatypes.experience.XPGainReason;
|
||||
import com.gmail.nossr50.datatypes.experience.XPGainSource;
|
||||
import com.gmail.nossr50.datatypes.interactions.NotificationType;
|
||||
import com.gmail.nossr50.datatypes.meta.RuptureTaskMeta;
|
||||
import com.gmail.nossr50.datatypes.party.Party;
|
||||
import com.gmail.nossr50.datatypes.party.PartyTeleportRecord;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
@ -48,7 +47,6 @@ import com.gmail.nossr50.skills.unarmed.UnarmedManager;
|
||||
import com.gmail.nossr50.skills.woodcutting.WoodcuttingManager;
|
||||
import com.gmail.nossr50.util.BlockUtils;
|
||||
import com.gmail.nossr50.util.EventUtils;
|
||||
import com.gmail.nossr50.util.MetadataConstants;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.experience.ExperienceBarManager;
|
||||
@ -1296,17 +1294,9 @@ public class McMMOPlayer implements Identified {
|
||||
*/
|
||||
public void logout(boolean syncSave) {
|
||||
final Player thisPlayer = getPlayer();
|
||||
if (thisPlayer.hasMetadata(MetadataConstants.METADATA_KEY_RUPTURE)) {
|
||||
final RuptureTaskMeta ruptureTaskMeta
|
||||
= (RuptureTaskMeta) thisPlayer.getMetadata(
|
||||
MetadataConstants.METADATA_KEY_RUPTURE).get(0);
|
||||
if (ruptureTaskMeta != null) {
|
||||
final RuptureTask ruptureTimerTask = ruptureTaskMeta.getRuptureTimerTask();
|
||||
if (ruptureTimerTask != null) {
|
||||
ruptureTimerTask.cancel();
|
||||
}
|
||||
thisPlayer.removeMetadata(MetadataConstants.METADATA_KEY_RUPTURE, mcMMO.p);
|
||||
}
|
||||
final RuptureTask ruptureTask = RuptureTask.getActive(thisPlayer);
|
||||
if (ruptureTask != null) {
|
||||
ruptureTask.cancel();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
@ -6,21 +6,29 @@ import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.events.skills.rupture.McMMOEntityDamageByRuptureEvent;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.CancellableRunnable;
|
||||
import com.gmail.nossr50.util.MetadataConstants;
|
||||
import com.gmail.nossr50.util.MobHealthbarUtils;
|
||||
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
||||
import com.gmail.nossr50.util.sounds.SoundManager;
|
||||
import com.gmail.nossr50.util.sounds.SoundType;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.bukkit.attribute.AttributeInstance;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class RuptureTask extends CancellableRunnable {
|
||||
|
||||
public static final int DAMAGE_TICK_INTERVAL = 10;
|
||||
public static final int ANIMATION_TICK_INTERVAL = 1;
|
||||
|
||||
// Active rupture per target entity.
|
||||
private static final @NotNull Map<UUID, RuptureTask> ACTIVE_RUPTURES =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
private final @NotNull McMMOPlayer ruptureSource;
|
||||
private final @NotNull LivingEntity targetEntity;
|
||||
private final int expireTick;
|
||||
@ -53,6 +61,20 @@ public class RuptureTask extends CancellableRunnable {
|
||||
this.pureTickDamage = pureTickDamage;
|
||||
}
|
||||
|
||||
public static @Nullable RuptureTask getActive(@NotNull Entity target) {
|
||||
return ACTIVE_RUPTURES.get(target.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this rupture as the target's active bleed and starts it ticking.
|
||||
* The task removes itself from the registry when it is cancelled; on Folia the
|
||||
* retired callback covers entities that are removed before the next tick.
|
||||
*/
|
||||
public void schedule() {
|
||||
ACTIVE_RUPTURES.put(targetEntity.getUniqueId(), this);
|
||||
mcMMO.p.getFoliaLib().getScheduler().runAtEntityTimer(targetEntity, this, this::cancel, 1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated constructor for the RuptureTask class.
|
||||
*
|
||||
@ -106,10 +128,15 @@ public class RuptureTask extends CancellableRunnable {
|
||||
}
|
||||
} else {
|
||||
this.cancel(); //Task no longer needed
|
||||
targetEntity.removeMetadata(MetadataConstants.METADATA_KEY_RUPTURE, mcMMO.p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
ACTIVE_RUPTURES.remove(targetEntity.getUniqueId(), this);
|
||||
super.cancel();
|
||||
}
|
||||
|
||||
private void playAnimation() {
|
||||
if (animationTick >= ANIMATION_TICK_INTERVAL) {
|
||||
ParticleEffectUtils.playBleedEffect(targetEntity); //Animate
|
||||
@ -176,7 +203,6 @@ public class RuptureTask extends CancellableRunnable {
|
||||
}
|
||||
|
||||
private void endRupture() {
|
||||
targetEntity.removeMetadata(MetadataConstants.METADATA_KEY_RUPTURE, mcMMO.p);
|
||||
this.cancel(); //Task no longer needed
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.gmail.nossr50.skills.swords;
|
||||
|
||||
import com.gmail.nossr50.datatypes.interactions.NotificationType;
|
||||
import com.gmail.nossr50.datatypes.meta.RuptureTaskMeta;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
@ -10,7 +9,6 @@ import com.gmail.nossr50.datatypes.skills.ToolType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.runnables.skills.RuptureTask;
|
||||
import com.gmail.nossr50.skills.SkillManager;
|
||||
import com.gmail.nossr50.util.MetadataConstants;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.random.ProbabilityUtil;
|
||||
@ -81,17 +79,15 @@ public class SwordsManager extends SkillManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.hasMetadata(MetadataConstants.METADATA_KEY_RUPTURE)) {
|
||||
RuptureTaskMeta ruptureTaskMeta = (RuptureTaskMeta) target.getMetadata(
|
||||
MetadataConstants.METADATA_KEY_RUPTURE).get(0);
|
||||
|
||||
final RuptureTask ongoingRupture = RuptureTask.getActive(target);
|
||||
if (ongoingRupture != null) {
|
||||
if (mmoPlayer.isDebugMode()) {
|
||||
mmoPlayer.getPlayer()
|
||||
.sendMessage("Rupture task ongoing for target " + target);
|
||||
mmoPlayer.getPlayer().sendMessage(ruptureTaskMeta.getRuptureTimerTask().toString());
|
||||
mmoPlayer.getPlayer().sendMessage(ongoingRupture.toString());
|
||||
}
|
||||
|
||||
ruptureTaskMeta.getRuptureTimerTask().refreshRupture();
|
||||
ongoingRupture.refreshRupture();
|
||||
return; //Don't apply bleed
|
||||
}
|
||||
|
||||
@ -118,10 +114,7 @@ public class SwordsManager extends SkillManager {
|
||||
mcMMO.p.getAdvancedConfig()
|
||||
.getRuptureTickDamage(target instanceof Player, getRuptureRank()));
|
||||
|
||||
final RuptureTaskMeta ruptureTaskMeta = new RuptureTaskMeta(mcMMO.p, ruptureTask);
|
||||
|
||||
mcMMO.p.getFoliaLib().getScheduler().runAtEntityTimer(target, ruptureTask, 1, 1);
|
||||
target.setMetadata(MetadataConstants.METADATA_KEY_RUPTURE, ruptureTaskMeta);
|
||||
ruptureTask.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,6 @@ public class MetadataConstants {
|
||||
* removed by {@link MobHealthbarUtils#restoreNameFromSnapshot}.
|
||||
*/
|
||||
public static final @NotNull Set<String> MOB_METADATA_KEYS = Set.of(
|
||||
MetadataConstants.METADATA_KEY_RUPTURE,
|
||||
MetadataConstants.METADATA_KEY_EXPLOSION_FROM_RUPTURE
|
||||
);
|
||||
|
||||
@ -66,7 +65,17 @@ public class MetadataConstants {
|
||||
public static final @NotNull String METADATA_KEY_VILLAGER_TRADE_ORIGIN_ITEM = "mcmmo_villager_trade_origin_item";
|
||||
public static final @NotNull String METADATA_KEY_EXPLOITED_ENDERMEN = "mcmmo_exploited_endermen";
|
||||
|
||||
/**
|
||||
* No longer used. mcMMO stopped applying this metadata to bleeding entities in 2.3.001,
|
||||
* so {@code hasMetadata} checks against this key always return false. Use
|
||||
* {@link com.gmail.nossr50.api.AbilityAPI#isBleeding(org.bukkit.entity.LivingEntity)} instead.
|
||||
*
|
||||
* @deprecated mcMMO no longer applies this metadata; see
|
||||
* {@link com.gmail.nossr50.api.AbilityAPI#isBleeding}.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2.3.001")
|
||||
public static final @NotNull String METADATA_KEY_RUPTURE = "mcmmo_rupture";
|
||||
|
||||
public static final byte SIMPLE_FLAG_VALUE = (byte) 0x1;
|
||||
|
||||
/**
|
||||
|
||||
@ -14,14 +14,12 @@ import static org.mockito.Mockito.when;
|
||||
import com.gmail.nossr50.MMOTestEnvironment;
|
||||
import com.gmail.nossr50.api.exceptions.InvalidSkillException;
|
||||
import com.gmail.nossr50.datatypes.interactions.NotificationType;
|
||||
import com.gmail.nossr50.datatypes.meta.RuptureTaskMeta;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
|
||||
import com.gmail.nossr50.datatypes.skills.ToolType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.runnables.skills.RuptureTask;
|
||||
import com.gmail.nossr50.util.MetadataConstants;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.random.ProbabilityUtil;
|
||||
@ -29,7 +27,9 @@ import com.gmail.nossr50.util.skills.CombatUtils;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
import com.tcoded.folialib.FoliaLib;
|
||||
import com.tcoded.folialib.impl.PlatformScheduler;
|
||||
import java.util.List;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -67,6 +67,7 @@ class SwordsManagerTest extends MMOTestEnvironment {
|
||||
|
||||
swordsManager = new SwordsManager(mmoPlayer);
|
||||
target = Mockito.mock(LivingEntity.class);
|
||||
when(target.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
|
||||
// Simulate Paper 26.1.2+ during a damage event: the ticker was already reset, so the
|
||||
// live cooldown misreports the charge of the committed hit.
|
||||
@ -86,7 +87,6 @@ class SwordsManagerTest extends MMOTestEnvironment {
|
||||
.thenReturn(true);
|
||||
Mockito.when(RankUtils.getRank(player, SubSkillType.SWORDS_RUPTURE)).thenReturn(3);
|
||||
when(advancedConfig.getRuptureChanceToApplyOnHit(3)).thenReturn(33.0);
|
||||
when(target.hasMetadata(MetadataConstants.METADATA_KEY_RUPTURE)).thenReturn(false);
|
||||
|
||||
try (MockedStatic<ProbabilityUtil> probabilityUtil = mockStatic(ProbabilityUtil.class)) {
|
||||
// When - a hit committed at half attack strength lands and the roll fails
|
||||
@ -171,18 +171,21 @@ class SwordsManagerTest extends MMOTestEnvironment {
|
||||
when(advancedConfig.getRuptureChanceToApplyOnHit(3)).thenReturn(100.0);
|
||||
}
|
||||
|
||||
// Seeds RuptureTask's active-rupture registry for the test.
|
||||
@SuppressWarnings("unchecked")
|
||||
private void trackRupture(Entity target, RuptureTask task) throws Exception {
|
||||
Field field = RuptureTask.class.getDeclaredField("ACTIVE_RUPTURES");
|
||||
field.setAccessible(true);
|
||||
((Map<UUID, RuptureTask>) field.get(null)).put(target.getUniqueId(), task);
|
||||
}
|
||||
|
||||
@Test
|
||||
void existingRuptureShouldBeRefreshedInsteadOfReRolled() {
|
||||
void existingRuptureShouldBeRefreshedInsteadOfReRolled() throws Exception {
|
||||
try (MockedStatic<ProbabilityUtil> probabilityUtil =
|
||||
mockStatic(ProbabilityUtil.class)) {
|
||||
// Given - the target is already bleeding from a rupture
|
||||
final RuptureTask ongoingRupture = Mockito.mock(RuptureTask.class);
|
||||
final RuptureTaskMeta ruptureMeta = Mockito.mock(RuptureTaskMeta.class);
|
||||
when(ruptureMeta.getRuptureTimerTask()).thenReturn(ongoingRupture);
|
||||
when(target.hasMetadata(MetadataConstants.METADATA_KEY_RUPTURE))
|
||||
.thenReturn(true);
|
||||
when(target.getMetadata(MetadataConstants.METADATA_KEY_RUPTURE))
|
||||
.thenReturn(List.of(ruptureMeta));
|
||||
trackRupture(target, ongoingRupture);
|
||||
|
||||
// When - another rupture-capable hit lands
|
||||
swordsManager.processRupture(target, 1.0);
|
||||
@ -207,10 +210,9 @@ class SwordsManagerTest extends MMOTestEnvironment {
|
||||
swordsManager.processRupture(target, 1.0);
|
||||
|
||||
// Then - a bleed task starts ticking on the target and is remembered on it
|
||||
verify(scheduler).runAtEntityTimer(eq(target), any(RuptureTask.class), eq(1L),
|
||||
eq(1L));
|
||||
verify(target).setMetadata(eq(MetadataConstants.METADATA_KEY_RUPTURE),
|
||||
any(RuptureTaskMeta.class));
|
||||
verify(scheduler).runAtEntityTimer(eq(target), any(RuptureTask.class),
|
||||
any(Runnable.class), eq(1L), eq(1L));
|
||||
assertThat(RuptureTask.getActive(target)).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,6 +222,7 @@ class SwordsManagerTest extends MMOTestEnvironment {
|
||||
mockStatic(ProbabilityUtil.class)) {
|
||||
// Given - a defending player who is blocking with a shield
|
||||
final Player defender = Mockito.mock(Player.class);
|
||||
when(defender.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
when(defender.isBlocking()).thenReturn(true);
|
||||
probabilityUtil.when(() -> ProbabilityUtil.isStaticSkillRNGSuccessful(
|
||||
eq(PrimarySkillType.SWORDS), eq(mmoPlayer), eq(100.0)))
|
||||
@ -229,8 +232,7 @@ class SwordsManagerTest extends MMOTestEnvironment {
|
||||
swordsManager.processRupture(defender, 1.0);
|
||||
|
||||
// Then - no bleed starts
|
||||
verify(defender, never()).setMetadata(eq(MetadataConstants.METADATA_KEY_RUPTURE),
|
||||
any());
|
||||
assertThat(RuptureTask.getActive(defender)).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,6 +242,7 @@ class SwordsManagerTest extends MMOTestEnvironment {
|
||||
mockStatic(ProbabilityUtil.class)) {
|
||||
// Given - a non-blocking defending player who uses notifications
|
||||
final Player defender = Mockito.mock(Player.class);
|
||||
when(defender.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
when(NotificationManager.doesPlayerUseNotifications(defender)).thenReturn(true);
|
||||
probabilityUtil.when(() -> ProbabilityUtil.isStaticSkillRNGSuccessful(
|
||||
eq(PrimarySkillType.SWORDS), eq(mmoPlayer), eq(100.0)))
|
||||
|
||||
Reference in New Issue
Block a user