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. --> <!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<!-- This allows to change between versions. --> <!-- This allows to change between versions. -->
<build.version>1.14.1</build.version> <build.version>1.14.2</build.version>
</properties> </properties>
<!-- Profiles will allow to automatically change build version. --> <!-- Profiles will allow to automatically change build version. -->

View File

@ -1,34 +1,33 @@
package world.bentobox.acidisland.world; package world.bentobox.acidisland.world;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.stream.Stream; import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Animals; import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.MagmaCube; import org.bukkit.entity.MagmaCube;
import org.bukkit.entity.Monster; import org.bukkit.entity.Monster;
import org.bukkit.entity.WaterMob;
import org.bukkit.scheduler.BukkitTask;
import world.bentobox.acidisland.AcidIsland; import world.bentobox.acidisland.AcidIsland;
import world.bentobox.acidisland.listeners.AcidEffect; import world.bentobox.acidisland.listeners.AcidEffect;
public class AcidTask { public class AcidTask {
private final AcidIsland addon; private final AcidIsland addon;
private static final List<EntityType> IMMUNE = Arrays.asList(EntityType.GUARDIAN, EntityType.ELDER_GUARDIAN, private static final List<EntityType> IMMUNE = Arrays.asList(EntityType.TURTLE, EntityType.POLAR_BEAR, EntityType.DROWNED);
EntityType.SQUID, EntityType.TURTLE, EntityType.POLAR_BEAR, EntityType.DROWNED); private Map<Entity, Long> itemsInWater = new ConcurrentHashMap<>();
private Set<Entity> itemsInWater = Collections.newSetFromMap(new WeakHashMap<Entity, Boolean>()); private BukkitTask findMobsTask;
private int entityBurnTask = -1;
private int itemBurnTask = -1;
/** /**
* Runs repeating tasks to deliver acid damage to mobs, etc. * Runs repeating tasks to deliver acid damage to mobs, etc.
@ -36,85 +35,91 @@ public class AcidTask {
*/ */
public AcidTask(AcidIsland addon) { public AcidTask(AcidIsland addon) {
this.addon = addon; this.addon = addon;
burnEntities(); findMobsTask = Bukkit.getScheduler().runTaskTimerAsynchronously(addon.getPlugin(), () -> findEntities(), 0L, 20L);
runAcidItemRemovalTask();
} }
/** void findEntities() {
* Start the entity burning task Map<Entity, Long> burnList = new WeakHashMap<>();
*/ for (Entity e : getEntityStream()) {
private void burnEntities() { if (e instanceof Item || (!IMMUNE.contains(e.getType()) && !(e instanceof WaterMob))) {
// This part will kill monsters if they fall into the water because it is acid int x = e.getLocation().getBlockX() >> 4;
entityBurnTask = Bukkit.getScheduler().scheduleSyncRepeatingTask(addon.getPlugin(), () -> getEntityStream() int z = e.getLocation().getBlockZ() >> 4;
// These entities are immune to acid if (e.getWorld().isChunkLoaded(x,z)) {
.filter(e -> !IMMUNE.contains(e.getType())) if (e.getLocation().getBlock().getType().equals(Material.WATER)) {
// Only burn if the chunk is loaded if ((e instanceof Monster || e instanceof MagmaCube) && addon.getSettings().getAcidDamageMonster() > 0D) {
.filter(e -> e.getLocation().getChunk().isLoaded()) burnList.put(e, (long)addon.getSettings().getAcidDamageMonster());
.filter(w -> w.getLocation().getBlock().getType().equals(Material.WATER))
.forEach(e -> { } else if ((e instanceof Animals) && addon.getSettings().getAcidDamageAnimal() > 0D
if ((e instanceof Monster || e instanceof MagmaCube) && addon.getSettings().getAcidDamageMonster() > 0D) { && (!e.getType().equals(EntityType.CHICKEN) || addon.getSettings().isAcidDamageChickens())) {
applyDamage((LivingEntity) e, addon.getSettings().getAcidDamageMonster()); burnList.put(e, (long)addon.getSettings().getAcidDamageAnimal());
} else if ((e instanceof Animals) && addon.getSettings().getAcidDamageAnimal() > 0D } else if (addon.getSettings().getAcidDestroyItemTime() > 0 && e instanceof Item) {
&& (!e.getType().equals(EntityType.CHICKEN) || addon.getSettings().isAcidDamageChickens())) { burnList.put(e, System.currentTimeMillis());
((LivingEntity) e).damage(addon.getSettings().getAcidDamageAnimal()); }
} }
}), 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) { void applyDamage(Entity e, long damage) {
e.damage(Math.max(0, damage - damage * AcidEffect.getDamageReduced(e))); 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. * @return a stream of all entities in this world and the nether and end if those are island worlds too.
*/ */
private Stream<Entity> getEntityStream() { List<Entity> getEntityStream() {
Stream<Entity> entityStream = addon.getOverWorld().getEntities().stream(); List<Entity> entityStream = new ArrayList<>(addon.getOverWorld().getEntities());
// Nether and end // Nether and end
if (addon.getSettings().isNetherGenerate() && addon.getSettings().isNetherIslands()) { 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()) { if (addon.getSettings().isEndGenerate() && addon.getSettings().isEndIslands()) {
entityStream = Stream.concat(entityStream, addon.getEndWorld().getEntities().stream()); entityStream.addAll(addon.getEndWorld().getEntities());
} }
return entityStream; 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 * Cancel tasks running
*/ */
public void cancelTasks() { public void cancelTasks() {
if (entityBurnTask >= 0) { if (findMobsTask != null) findMobsTask.cancel();
Bukkit.getScheduler().cancelTask(entityBurnTask); }
}
if (itemBurnTask >= 0) { /**
Bukkit.getScheduler().cancelTask(itemBurnTask); * @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; 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.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq; 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.verify;
import static org.mockito.Mockito.when; 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.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.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito; import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; 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.AISettings;
import world.bentobox.acidisland.AcidIsland; import world.bentobox.acidisland.AcidIsland;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class}) @PrepareForTest({Bukkit.class})
public class AcidTaskTest { public class AcidTaskTest {
@Mock
private BukkitScheduler scheduler;
@Mock
private AISettings settings;
@Mock @Mock
private AcidIsland addon; 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 @Before
public void setUp() throws Exception { public void setUp() throws Exception {
PowerMockito.mockStatic(Bukkit.class); PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
when(Bukkit.getScheduler()).thenReturn(scheduler); 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); 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 @Test
public void testAcidTaskDoNotDestroyItems() { public void testAcidTask() {
new AcidTask(addon); verify(scheduler).runTaskTimerAsynchronously(eq(null), any(Runnable.class), eq(0L), eq(20L));
verify(scheduler).scheduleSyncRepeatingTask(any(), any(Runnable.class), eq(0L), eq(20L));
} }
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#findEntities()}.
*/
@Test @Test
public void testAcidTaskDestroyItems() { public void testFindEntities() {
when(settings.getAcidDestroyItemTime()).thenReturn(5L);
new AcidTask(addon); at.findEntities();
verify(scheduler).scheduleSyncRepeatingTask(any(), any(Runnable.class), eq(0L), eq(20L)); verify(scheduler).runTask(eq(null), any(Runnable.class));
verify(scheduler).scheduleSyncRepeatingTask(any(), any(Runnable.class), eq(100L), eq(100L));
} }
/**
* Test method for {@link world.bentobox.acidisland.world.AcidTask#applyDamage(org.bukkit.entity.Entity, long)}.
*/
@Test @Test
public void testAcidTaskCancelTasks() { public void testApplyDamageRemoveItems() {
AcidTask task = new AcidTask(addon); Item e = mock(Item.class);
task.cancelTasks(); when(e.getLocation()).thenReturn(l);
verify(scheduler).cancelTask(anyInt()); 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 @Test
public void testAcidTaskCancelBothTasks() { public void testApplyDamageNoItemDamage() {
when(settings.getAcidDestroyItemTime()).thenReturn(5L); settings.setAcidDestroyItemTime(0L);
AcidTask task = new AcidTask(addon); Item e = mock(Item.class);
task.cancelTasks(); at.applyDamage(e, 0);
verify(scheduler, times(2)).cancelTask(anyInt());
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();
}
} }