Fixing Rupture (Bleed) ConcurrentModificationException

Removed the internal limit on ticks for Rupture
Rupture no longer adds new ticks when applying bleed to an existing target, it instead refreshes the duration
This commit is contained in:
nossr50 2019-01-25 11:12:31 -08:00
parent a421bd7e4c
commit c22a1a0dd2
4 changed files with 64 additions and 81 deletions

View File

@ -37,8 +37,10 @@ Version 2.1.0
+ (MySQL) Added support for SSL for MySQL/MariaDB (On by default) + (MySQL) Added support for SSL for MySQL/MariaDB (On by default)
! (Skills) Taming's Gore now uses Rupture Rank 1 for its DoT calculations ! (Skills) Taming's Gore now uses Rupture Rank 1 for its DoT calculations
! (Skills) Sword's Bleed has been renamed to Rupture ! (Skills) Sword's Bleed has been renamed to Rupture
! (Skills) Sword's Rupture no longer has an internal hard coded limit
! (Skills) Sword's Serrated Strikes now uses your Rupture rank to determine the damage/ticks for its bleed effect. ! (Skills) Sword's Serrated Strikes now uses your Rupture rank to determine the damage/ticks for its bleed effect.
! (Skills) Sword's Rupture now ticks four times as fast ! (Skills) Sword's Rupture now ticks four times as fast
! (Skills) Sword's Rupture now refreshes bleed duration instead of adding duration when applying bleed to the same target
! (Skills) Sword's Rupture will now deal lethal damage ! (Skills) Sword's Rupture will now deal lethal damage
= (Skills) Fixed a bug where Rupture would apply an incorrect amount of bleed ticks = (Skills) Fixed a bug where Rupture would apply an incorrect amount of bleed ticks
! (Skills) Sword's Rupture now reaches its max proc chance at level 20 (200 in Retro) ! (Skills) Sword's Rupture now reaches its max proc chance at level 20 (200 in Retro)

View File

@ -527,7 +527,6 @@ public class EntityListener implements Listener {
return; return;
} }
BleedTimerTask.remove(entity);
Archery.arrowRetrievalCheck(entity); Archery.arrowRetrievalCheck(entity);
} }

View File

@ -0,0 +1,18 @@
package com.gmail.nossr50.runnables.skills;
import org.bukkit.entity.LivingEntity;
public class BleedContainer {
public int bleedTicks;
public int bleedRank;
public LivingEntity target;
public LivingEntity damageSource;
public BleedContainer(LivingEntity target, int bleedTicks, int bleedRank, LivingEntity damageSource)
{
this.target = target;
this.bleedTicks = bleedTicks;
this.bleedRank = bleedRank;
this.damageSource = damageSource;
}
}

View File

@ -1,8 +1,10 @@
package com.gmail.nossr50.runnables.skills; package com.gmail.nossr50.runnables.skills;
import com.gmail.nossr50.config.AdvancedConfig; import com.gmail.nossr50.config.AdvancedConfig;
import com.gmail.nossr50.datatypes.interactions.NotificationType;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.MobHealthbarUtils; import com.gmail.nossr50.util.MobHealthbarUtils;
import com.gmail.nossr50.util.player.NotificationManager;
import com.gmail.nossr50.util.skills.CombatUtils; import com.gmail.nossr50.util.skills.CombatUtils;
import com.gmail.nossr50.util.skills.ParticleEffectUtils; import com.gmail.nossr50.util.skills.ParticleEffectUtils;
import com.gmail.nossr50.util.sounds.SoundManager; import com.gmail.nossr50.util.sounds.SoundManager;
@ -11,29 +13,26 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
public class BleedTimerTask extends BukkitRunnable { public class BleedTimerTask extends BukkitRunnable {
private final static int MAX_BLEED_TICKS = 100; //The cap has been raised :) private static Map<LivingEntity, BleedContainer> bleedList = new HashMap<LivingEntity, BleedContainer>();
private static Map<LivingEntity, Integer> bleedList = new HashMap<LivingEntity, Integer>();
private static Map<LivingEntity, Integer> bleedDamage = new HashMap<LivingEntity, Integer>();
private static Map<LivingEntity, LivingEntity> attackerMap = new HashMap<>();
private static ArrayList<LivingEntity> cleanupList = new ArrayList<>();
private static ArrayList<LivingEntity> lowerList = new ArrayList<>();
@Override @Override
public void run() { synchronized public void run() {
lowerBleedTicks(); //Lower bleed ticks Iterator<Entry<LivingEntity, BleedContainer>> bleedIterator = bleedList.entrySet().iterator();
cleanEntities(); //Remove unwanted entities
for(LivingEntity target : bleedList.keySet()) while (bleedIterator.hasNext()) {
{ Entry<LivingEntity, BleedContainer> containerEntry = bleedIterator.next();
//mcMMO.p.getServer().broadcastMessage("Entity "+target.getName()+" has "+bleedList.get(target)+" ticks of bleed left"); LivingEntity target = containerEntry.getKey();
if (bleedList.get(target) <= 0 || !target.isValid()) { int bleedTicks = containerEntry.getValue().bleedTicks;
cleanupList.add(target);
if (containerEntry.getValue().bleedTicks <= 0 || !target.isValid()) {
bleedIterator.remove();
continue; continue;
} }
@ -43,59 +42,49 @@ public class BleedTimerTask extends BukkitRunnable {
damage = AdvancedConfig.getInstance().getRuptureDamagePlayer(); damage = AdvancedConfig.getInstance().getRuptureDamagePlayer();
//Above Bleed Rank 3 deals 50% more damage //Above Bleed Rank 3 deals 50% more damage
if(bleedDamage.get(target) >= 3) if (containerEntry.getValue().bleedRank >= 3)
damage = damage * 1.5; damage = damage * 1.5;
Player player = (Player) target; Player player = (Player) target;
if (!player.isOnline()) { if (!player.isOnline()) {
cleanupList.add(target);
continue; continue;
} }
/*if (bleedList.get(target) <= 0) { NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding.Stopped");
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding.Stopped"); } else {
}*/
}
else {
damage = AdvancedConfig.getInstance().getRuptureDamageMobs(); damage = AdvancedConfig.getInstance().getRuptureDamageMobs();
//Above Bleed Rank 3 deals 50% more damage
if (containerEntry.getValue().bleedRank >= 3)
damage = damage * 1.5;
MobHealthbarUtils.handleMobHealthbars(target, damage, mcMMO.p); //Update health bars MobHealthbarUtils.handleMobHealthbars(target, damage, mcMMO.p); //Update health bars
} }
CombatUtils.dealNoInvulnerabilityTickDamage(target, damage, attackerMap.get(target)); CombatUtils.dealNoInvulnerabilityTickDamage(target, damage, containerEntry.getValue().damageSource);
//Play Bleed Sound //Play Bleed Sound
SoundManager.worldSendSound(target.getWorld(), target.getLocation(), SoundType.BLEED); SoundManager.worldSendSound(target.getWorld(), target.getLocation(), SoundType.BLEED);
ParticleEffectUtils.playBleedEffect(target); ParticleEffectUtils.playBleedEffect(target);
lowerBleedDurationTicks(target);
//Lower Bleed Ticks
BleedContainer loweredBleedContainer = copyContainer(containerEntry.getValue());
loweredBleedContainer.bleedTicks -= 1;
containerEntry.setValue(loweredBleedContainer);
} }
} }
private void lowerBleedTicks() { public static BleedContainer copyContainer(BleedContainer container)
for(LivingEntity lower : lowerList) {
{ LivingEntity target = container.target;
if(bleedList.containsKey(lower)) LivingEntity source = container.damageSource;
bleedList.put(lower, bleedList.get(lower) - 1); int bleedTicks = container.bleedTicks;
} int bleedRank = container.bleedRank;
lowerList.clear(); BleedContainer newContainer = new BleedContainer(target, bleedTicks, bleedRank, source);
} return newContainer;
private void cleanEntities() {
for(LivingEntity cleanTarget : cleanupList)
{
if(bleedList.containsKey(cleanTarget))
{
remove(cleanTarget);
}
}
cleanupList.clear(); //Reset List
}
private void lowerBleedDurationTicks(LivingEntity target) {
if(bleedList.get(target) != null)
lowerList.add(target);
} }
/** /**
@ -104,24 +93,12 @@ public class BleedTimerTask extends BukkitRunnable {
* @param entity LivingEntity to bleed out * @param entity LivingEntity to bleed out
*/ */
public static void bleedOut(LivingEntity entity) { public static void bleedOut(LivingEntity entity) {
if (bleedList.containsKey(entity)) { /*
CombatUtils.dealNoInvulnerabilityTickDamage(entity, bleedList.get(entity) * 2, attackerMap.get(entity)); * Don't remove anything from the list outside of run()
bleedList.remove(entity); */
bleedDamage.remove(entity);
attackerMap.remove(entity);
}
}
/**
* Remove a LivingEntity from the bleedList if it is in it
*
* @param entity LivingEntity to remove
*/
public static void remove(LivingEntity entity) {
if (bleedList.containsKey(entity)) { if (bleedList.containsKey(entity)) {
bleedList.remove(entity); CombatUtils.dealNoInvulnerabilityTickDamage(entity, bleedList.get(entity).bleedTicks * 2, bleedList.get(entity).damageSource);
bleedDamage.remove(entity);
attackerMap.remove(entity);
} }
} }
@ -131,22 +108,9 @@ public class BleedTimerTask extends BukkitRunnable {
* @param entity LivingEntity to add * @param entity LivingEntity to add
* @param ticks Number of bleeding ticks * @param ticks Number of bleeding ticks
*/ */
public static void add(LivingEntity entity, LivingEntity attacker, int ticks, int bleedRank) { public synchronized static void add(LivingEntity entity, LivingEntity attacker, int ticks, int bleedRank) {
int newTicks = ticks; BleedContainer newBleedContainer = new BleedContainer(entity, ticks, bleedRank, attacker);
bleedList.put(entity, newBleedContainer);
if (bleedList.containsKey(entity)) {
newTicks += bleedList.get(entity);
bleedList.put(entity, Math.min(MAX_BLEED_TICKS, newTicks));
//Override the current bleed rank only if this one is higher
if(bleedDamage.get(entity) < bleedRank)
bleedDamage.put(entity, bleedRank);
}
else {
bleedList.put(entity, Math.min(MAX_BLEED_TICKS, newTicks));
bleedDamage.put(entity, bleedRank);
attackerMap.put(entity, attacker);
}
} }
public static boolean isBleeding(LivingEntity entity) { public static boolean isBleeding(LivingEntity entity) {