Merge remote-tracking branch 'origin/develop'

Conflicts:
	pom.xml
This commit is contained in:
tastybento 2020-08-12 20:44:38 -07:00
commit c896925201
3 changed files with 268 additions and 93 deletions

View File

@ -65,7 +65,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.14.1</build.version>
<build.version>1.14.2</build.version>
</properties>
<!-- Profiles will allow to automatically change build version. -->

View File

@ -1,34 +1,33 @@
package world.bentobox.acidisland.world;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.stream.Stream;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.MagmaCube;
import org.bukkit.entity.Monster;
import org.bukkit.entity.WaterMob;
import org.bukkit.scheduler.BukkitTask;
import world.bentobox.acidisland.AcidIsland;
import world.bentobox.acidisland.listeners.AcidEffect;
public class AcidTask {
private final AcidIsland addon;
private static final List<EntityType> IMMUNE = Arrays.asList(EntityType.GUARDIAN, EntityType.ELDER_GUARDIAN,
EntityType.SQUID, EntityType.TURTLE, EntityType.POLAR_BEAR, EntityType.DROWNED);
private Set<Entity> itemsInWater = Collections.newSetFromMap(new WeakHashMap<Entity, Boolean>());
private int entityBurnTask = -1;
private int itemBurnTask = -1;
private static final List<EntityType> IMMUNE = Arrays.asList(EntityType.TURTLE, EntityType.POLAR_BEAR, EntityType.DROWNED);
private Map<Entity, Long> itemsInWater = new ConcurrentHashMap<>();
private BukkitTask findMobsTask;
/**
* Runs repeating tasks to deliver acid damage to mobs, etc.
@ -36,85 +35,91 @@ public class AcidTask {
*/
public AcidTask(AcidIsland addon) {
this.addon = addon;
burnEntities();
runAcidItemRemovalTask();
findMobsTask = Bukkit.getScheduler().runTaskTimerAsynchronously(addon.getPlugin(), () -> findEntities(), 0L, 20L);
}
/**
* Start the entity burning task
*/
private void burnEntities() {
// This part will kill monsters if they fall into the water because it is acid
entityBurnTask = Bukkit.getScheduler().scheduleSyncRepeatingTask(addon.getPlugin(), () -> getEntityStream()
// These entities are immune to acid
.filter(e -> !IMMUNE.contains(e.getType()))
// Only burn if the chunk is loaded
.filter(e -> e.getLocation().getChunk().isLoaded())
.filter(w -> w.getLocation().getBlock().getType().equals(Material.WATER))
.forEach(e -> {
if ((e instanceof Monster || e instanceof MagmaCube) && addon.getSettings().getAcidDamageMonster() > 0D) {
applyDamage((LivingEntity) e, addon.getSettings().getAcidDamageMonster());
} else if ((e instanceof Animals) && addon.getSettings().getAcidDamageAnimal() > 0D
&& (!e.getType().equals(EntityType.CHICKEN) || addon.getSettings().isAcidDamageChickens())) {
((LivingEntity) e).damage(addon.getSettings().getAcidDamageAnimal());
void findEntities() {
Map<Entity, Long> burnList = new WeakHashMap<>();
for (Entity e : getEntityStream()) {
if (e instanceof Item || (!IMMUNE.contains(e.getType()) && !(e instanceof WaterMob))) {
int x = e.getLocation().getBlockX() >> 4;
int z = e.getLocation().getBlockZ() >> 4;
if (e.getWorld().isChunkLoaded(x,z)) {
if (e.getLocation().getBlock().getType().equals(Material.WATER)) {
if ((e instanceof Monster || e instanceof MagmaCube) && addon.getSettings().getAcidDamageMonster() > 0D) {
burnList.put(e, (long)addon.getSettings().getAcidDamageMonster());
} else if ((e instanceof Animals) && addon.getSettings().getAcidDamageAnimal() > 0D
&& (!e.getType().equals(EntityType.CHICKEN) || addon.getSettings().isAcidDamageChickens())) {
burnList.put(e, (long)addon.getSettings().getAcidDamageAnimal());
} else if (addon.getSettings().getAcidDestroyItemTime() > 0 && e instanceof Item) {
burnList.put(e, System.currentTimeMillis());
}
}
}), 0L, 20L);
}
}
}
// Remove any entities not on the burn list
itemsInWater.keySet().removeIf(i -> !burnList.keySet().contains(i));
if (!burnList.isEmpty()) {
Bukkit.getScheduler().runTask(addon.getPlugin(), () ->
// Burn everything
burnList.forEach(this::applyDamage));
}
}
private void applyDamage(LivingEntity e, double damage) {
e.damage(Math.max(0, damage - damage * AcidEffect.getDamageReduced(e)));
void applyDamage(Entity e, long damage) {
if (e instanceof LivingEntity) {
((LivingEntity)e).damage(Math.max(0, damage - damage * AcidEffect.getDamageReduced((LivingEntity)e)));
} else if (addon.getSettings().getAcidDestroyItemTime() > 0){
// Item
if (e.getLocation().getBlock().getType().equals(Material.WATER)) {
itemsInWater.putIfAbsent(e, damage + addon.getSettings().getAcidDestroyItemTime() * 1000);
if (System.currentTimeMillis() > itemsInWater.get(e)) {
e.getWorld().playSound(e.getLocation(), Sound.ENTITY_CREEPER_PRIMED, 3F, 3F);
e.remove();
itemsInWater.remove(e);
}
} else {
itemsInWater.remove(e);
}
}
}
/**
* @return a stream of all entities in this world and the nether and end if those are island worlds too.
*/
private Stream<Entity> getEntityStream() {
Stream<Entity> entityStream = addon.getOverWorld().getEntities().stream();
List<Entity> getEntityStream() {
List<Entity> entityStream = new ArrayList<>(addon.getOverWorld().getEntities());
// Nether and end
if (addon.getSettings().isNetherGenerate() && addon.getSettings().isNetherIslands()) {
entityStream = Stream.concat(entityStream, addon.getNetherWorld().getEntities().stream());
entityStream.addAll(addon.getNetherWorld().getEntities());
}
if (addon.getSettings().isEndGenerate() && addon.getSettings().isEndIslands()) {
entityStream = Stream.concat(entityStream, addon.getEndWorld().getEntities().stream());
entityStream.addAll(addon.getEndWorld().getEntities());
}
return entityStream;
}
/**
* Start the item removal in acid task
*/
private void runAcidItemRemovalTask() {
if (addon.getSettings().getAcidDestroyItemTime() <= 0) {
return;
}
itemBurnTask = Bukkit.getScheduler().scheduleSyncRepeatingTask(addon.getPlugin(), () -> {
Set<Entity> newItemsInWater = new HashSet<>();
getEntityStream()
.filter(e -> e.getType().equals(EntityType.DROPPED_ITEM))
.filter(e -> e.getLocation().getChunk().isLoaded())
.filter(e -> e.getLocation().getBlock().getType().equals(Material.WATER)
|| (e.getLocation().getY() > 0 && e.getLocation().getBlock().getRelative(BlockFace.DOWN).getType().equals(Material.WATER)))
.forEach(e -> {
if (itemsInWater.contains(e)) {
e.getWorld().playSound(e.getLocation(), Sound.ENTITY_CREEPER_PRIMED, 3F, 3F);
e.remove();
} else {
newItemsInWater.add(e);
}
});
itemsInWater = newItemsInWater;
}, addon.getSettings().getAcidDestroyItemTime() * 20L, addon.getSettings().getAcidDestroyItemTime() * 20L);
}
/**
* Cancel tasks running
*/
public void cancelTasks() {
if (entityBurnTask >= 0) {
Bukkit.getScheduler().cancelTask(entityBurnTask);
}
if (itemBurnTask >= 0) {
Bukkit.getScheduler().cancelTask(itemBurnTask);
}
if (findMobsTask != null) findMobsTask.cancel();
}
/**
* @return the itemsInWater
*/
Map<Entity, Long> getItemsInWater() {
return itemsInWater;
}
/**
* @param itemsInWater the itemsInWater to set
*/
void setItemsInWater(Map<Entity, Long> itemsInWater) {
this.itemsInWater = itemsInWater;
}
}

View File

@ -1,18 +1,43 @@
package world.bentobox.acidisland.world;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Cod;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item;
import org.bukkit.entity.Skeleton;
import org.bukkit.entity.Squid;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@ -20,53 +45,198 @@ import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.acidisland.AISettings;
import world.bentobox.acidisland.AcidIsland;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class})
public class AcidTaskTest {
@Mock
private BukkitScheduler scheduler;
@Mock
private AISettings settings;
@Mock
private AcidIsland addon;
// CUT
private AcidTask at;
private @Nullable AISettings settings;
@Mock
private World world;
private List<Entity> mob;
@Mock
private @Nullable World nether;
@Mock
private @Nullable World end;
@Mock
private BukkitScheduler scheduler;
@Mock
private BukkitTask task;
@Mock
private Location l;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
PowerMockito.mockStatic(Bukkit.class);
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
when(Bukkit.getScheduler()).thenReturn(scheduler);
when(settings.getAcidDestroyItemTime()).thenReturn(0L);
when(scheduler.runTaskTimerAsynchronously(any(), any(Runnable.class), anyLong(), anyLong())).thenReturn(task);
when(addon.getOverWorld()).thenReturn(world);
when(addon.getNetherWorld()).thenReturn(nether);
when(addon.getEndWorld()).thenReturn(end);
when(world.isChunkLoaded(anyInt(), anyInt())).thenReturn(true);
Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.WATER);
when(l.getBlock()).thenReturn(block);
// Default squid
mob = new ArrayList<>();
Squid squid = mock(Squid.class);
when(squid.getType()).thenReturn(EntityType.SQUID);
when(squid.getLocation()).thenReturn(l);
when(squid.getWorld()).thenReturn(world);
mob.add(squid);
Skeleton s = mock(Skeleton.class);
when(s.getLocation()).thenReturn(l);
when(s.getType()).thenReturn(EntityType.SKELETON);
when(s.getWorld()).thenReturn(world);
mob.add(s);
Cod c = mock(Cod.class);
when(c.getLocation()).thenReturn(l);
when(c.getType()).thenReturn(EntityType.COD);
when(c.getWorld()).thenReturn(world);
mob.add(mock(Cod.class));
Item i = mock(Item.class);
when(i.getLocation()).thenReturn(l);
when(i.getType()).thenReturn(EntityType.DROPPED_ITEM);
when(i.getWorld()).thenReturn(world);
mob.add(i);
when(world.getEntities()).thenReturn(mob);
when(nether.getEntities()).thenReturn(mob);
when(end.getEntities()).thenReturn(mob);
settings = new AISettings();
settings.setNetherGenerate(true);
settings.setEndGenerate(true);
settings.setEndIslands(true);
settings.setNetherIslands(true);
settings.setAcidDestroyItemTime(1);
settings.setAcidDamageAnimal(10);
settings.setAcidDamageMonster(10);
settings.setAcidDamageChickens(true);
when(addon.getSettings()).thenReturn(settings);
at = new AcidTask(addon);
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#AcidTask(world.bentobox.acidisland.AcidIsland)}.
*/
@Test
public void testAcidTaskDoNotDestroyItems() {
new AcidTask(addon);
verify(scheduler).scheduleSyncRepeatingTask(any(), any(Runnable.class), eq(0L), eq(20L));
public void testAcidTask() {
verify(scheduler).runTaskTimerAsynchronously(eq(null), any(Runnable.class), eq(0L), eq(20L));
}
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#findEntities()}.
*/
@Test
public void testAcidTaskDestroyItems() {
when(settings.getAcidDestroyItemTime()).thenReturn(5L);
new AcidTask(addon);
verify(scheduler).scheduleSyncRepeatingTask(any(), any(Runnable.class), eq(0L), eq(20L));
verify(scheduler).scheduleSyncRepeatingTask(any(), any(Runnable.class), eq(100L), eq(100L));
public void testFindEntities() {
at.findEntities();
verify(scheduler).runTask(eq(null), any(Runnable.class));
}
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#applyDamage(org.bukkit.entity.Entity, long)}.
*/
@Test
public void testAcidTaskCancelTasks() {
AcidTask task = new AcidTask(addon);
task.cancelTasks();
verify(scheduler).cancelTask(anyInt());
public void testApplyDamageRemoveItems() {
Item e = mock(Item.class);
when(e.getLocation()).thenReturn(l);
when(e.getWorld()).thenReturn(world);
// Put the item in the water
Map<Entity, Long> map = new HashMap<>();
map.put(e, 0L);
at.setItemsInWater(map);
at.applyDamage(e, 0);
verify(world).playSound(eq(l), any(Sound.class), anyFloat(), anyFloat());
verify(e).remove();
assertTrue(map.isEmpty());
}
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#applyDamage(org.bukkit.entity.Entity, long)}.
*/
@Test
public void testAcidTaskCancelBothTasks() {
when(settings.getAcidDestroyItemTime()).thenReturn(5L);
AcidTask task = new AcidTask(addon);
task.cancelTasks();
verify(scheduler, times(2)).cancelTask(anyInt());
public void testApplyDamageNoItemDamage() {
settings.setAcidDestroyItemTime(0L);
Item e = mock(Item.class);
at.applyDamage(e, 0);
verify(world, never()).playSound(any(), any(Sound.class), anyFloat(), anyFloat());
verify(e, never()).remove();
}
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#applyDamage(org.bukkit.entity.Entity, long)}.
*/
@Test
public void testApplyDamageKeepItems() {
Item e = mock(Item.class);
Location l = mock(Location.class);
Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.AIR);
when(l.getBlock()).thenReturn(block);
when(e.getLocation()).thenReturn(l);
when(e.getWorld()).thenReturn(world);
// Put the item in the water
Map<Entity, Long> map = new HashMap<>();
map.put(e, 0L);
at.setItemsInWater(map);
at.applyDamage(e, 0);
verify(world, never()).playSound(any(), any(Sound.class), anyFloat(), anyFloat());
verify(e, never()).remove();
assertTrue(map.isEmpty());
}
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#getEntityStream()}.
*/
@Test
public void testGetEntityStream() {
List<Entity> es = at.getEntityStream();
assertEquals(12, es.size());
}
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#cancelTasks()}.
*/
@Test
public void testCancelTasks() {
at.cancelTasks();
verify(task).cancel();
}
}