Fix ConcurrentModificationException [SD-8291] and registering task error

'registering task error' meaning exceptions for starting schedulers after plugin disabling (can still happens but far less spam)
This commit is contained in:
Christian Koop 2021-07-13 19:53:41 +02:00
parent ac48ed7d1a
commit 534090323d
No known key found for this signature in database
GPG Key ID: 89A8181384E010A3
13 changed files with 131 additions and 128 deletions

View File

@ -99,6 +99,9 @@ public class EpicFarming extends SongodaPlugin {
@Override
public void onPluginDisable() {
this.farmTask.cancel();
this.growthTask.cancel();
saveToFile();
for (Farm farm : farmManager.getFarms().values())
dataManager.updateItems(farm);
@ -169,8 +172,8 @@ public class EpicFarming extends SongodaPlugin {
}
// Start tasks
this.growthTask = GrowthTask.startTask(this);
this.farmTask = FarmTask.startTask(this);
this.growthTask = new GrowthTask(this);
this.farmTask = new FarmTask(this);
Bukkit.getScheduler().runTaskLater(this, () -> {
if (!Bukkit.getPluginManager().isPluginEnabled("EpicHoppers"))
@ -399,4 +402,4 @@ public class EpicFarming extends SongodaPlugin {
public DataManager getDataManager() {
return dataManager;
}
}
}

View File

@ -88,7 +88,7 @@ public class DataManager extends DataManagerAbstract {
String createItem = "INSERT INTO " + this.getTablePrefix() + "items (farm_id, item) VALUES (?, ?)";
try (PreparedStatement statement = connection.prepareStatement(createItem)) {
for (ItemStack item : farm.getItems()) {
for (ItemStack item : farm.getItems().toArray(new ItemStack[0])) {
statement.setInt(1, farm.getId());
statement.setBytes(2, ItemSerializer.serializeItem(item));
statement.addBatch();
@ -146,7 +146,7 @@ public class DataManager extends DataManagerAbstract {
String createItem = "INSERT INTO " + this.getTablePrefix() + "items (farm_id, item) VALUES (?, ?)";
try (PreparedStatement statement = connection.prepareStatement(createItem)) {
for (ItemStack item : farm.getItems()) {
for (ItemStack item : farm.getItems().toArray(new ItemStack[0])) {
statement.setInt(1, farm.getId());
statement.setBytes(2, ItemSerializer.serializeItem(item));
@ -211,4 +211,4 @@ public class DataManager extends DataManagerAbstract {
this.sync(() -> callback.accept(farms));
}));
}
}
}

View File

@ -30,7 +30,7 @@ public class Farm {
// This is the unique identifier for this farm.
// It is reset on every plugin load.
private UUID uniqueId = UUID.randomUUID();
private final UUID uniqueId = UUID.randomUUID();
// Id for database usage.
private int id;
@ -41,7 +41,7 @@ public class Farm {
private Location location;
private Level level;
private OverviewGui opened = null;
private UUID placedBy;
private final UUID placedBy;
private UUID viewing = null;
private long lastCached = 0;
@ -175,7 +175,6 @@ public class Farm {
b2.getWorld().playSound(b2.getLocation(), CompatibleSound.BLOCK_GRASS_BREAK.getSound(), 10, 15);
}, random.nextInt(30) + 1);
}
}
}
}
@ -192,50 +191,61 @@ public class Farm {
// Should be used in sync.
public void addItem(ItemStack toAdd) {
for (ItemStack item : new ArrayList<>(items)) {
if (item.getType() != toAdd.getType()
|| item.getAmount() + toAdd.getAmount() > item.getMaxStackSize()) continue;
item.setAmount(item.getAmount() + toAdd.getAmount());
if (opened != null)
opened.updateInventory();
return;
}
items.add(toAdd);
if (opened != null)
opened.updateInventory();
}
public void removeMaterial(Material material, int amount) {
for (ItemStack item : getItems()) {
if (material == item.getType()) {
item.setAmount(item.getAmount() - amount);
if (item.getAmount() <= 0)
this.items.remove(item);
synchronized (items) {
for (ItemStack item : new ArrayList<>(items)) {
if (item.getType() != toAdd.getType()
|| item.getAmount() + toAdd.getAmount() > item.getMaxStackSize()) continue;
item.setAmount(item.getAmount() + toAdd.getAmount());
if (opened != null)
opened.updateInventory();
return;
}
items.add(toAdd);
if (opened != null)
opened.updateInventory();
}
}
public boolean willFit(ItemStack item) {
if (items.size() < 27 * level.getPages()) return true;
public void removeMaterial(Material material, int amount) {
synchronized (items) {
for (ItemStack item : getItems().toArray(new ItemStack[0])) {
if (material == item.getType()) {
item.setAmount(item.getAmount() - amount);
for (ItemStack stack : items) {
if (stack.isSimilar(item) && stack.getAmount() < stack.getMaxStackSize()) {
return true;
if (item.getAmount() <= 0)
this.items.remove(item);
if (opened != null)
opened.updateInventory();
return;
}
}
}
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean willFit(ItemStack item) {
synchronized (items) {
if (items.size() < 27 * level.getPages()) return true;
for (ItemStack stack : items) {
if (stack.isSimilar(item) && stack.getAmount() < stack.getMaxStackSize()) {
return true;
}
}
}
return false;
}
public void setItems(List<ItemStack> items) {
this.items.clear();
this.items.addAll(items);
if (opened != null)
opened.updateInventory();
synchronized (this.items) {
this.items.clear();
this.items.addAll(items);
if (opened != null) {
opened.updateInventory();
}
}
}
public UUID getViewing() {
@ -270,6 +280,7 @@ public class Farm {
this.lastCached = lastCached;
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean isInLoadedChunk() {
return location != null && location.getWorld() != null && location.getWorld().isChunkLoaded(((int) location.getX()) >> 4, ((int) location.getZ()) >> 4);
}
@ -345,4 +356,4 @@ public class Farm {
public void setId(int id) {
this.id = id;
}
}
}

View File

@ -11,7 +11,6 @@ import com.songoda.epicfarming.utils.EntityInfo;
import com.songoda.epicfarming.utils.Methods;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.Entity;
@ -69,7 +68,6 @@ public class ModuleAutoBreeding extends Module {
counts.get(entity.getType()) - 1 + stackSize);
}
boolean mate1 = false;
for (Map.Entry<EntityType, Long> entry : counts.entrySet()) {
@ -89,8 +87,7 @@ public class ModuleAutoBreeding extends Module {
if (entry.getValue() >= 2) {
EntityType entityType = entry.getKey();
for (ItemStack item : new ArrayList<>(farm.getItems())) {
for (ItemStack item : farm.getItems().toArray(new ItemStack[0])) {
EntityInfo info = EntityInfo.of(entityType);
try {
if (info == null || item.getType() != info.getMaterial() || item.getAmount() < 2)

View File

@ -144,18 +144,20 @@ public class ModuleAutoCollect extends Module {
@Override
public void runButtonPress(Player player, Farm farm, ClickType type) {
if (type == ClickType.LEFT)
if (type == ClickType.LEFT) {
toggleEnabled(farm);
else if (type == ClickType.RIGHT)
} else if (type == ClickType.RIGHT) {
toggleCollectionType(farm);
}
}
@Override
public String getDescription() {
return plugin.getLocale().getMessage("interface.button.autocollect")
return plugin.getLocale()
.getMessage("interface.button.autocollect")
.processPlaceholder("status",
plugin.getLocale().getMessage("general.interface.unlocked")
.getMessage()).getMessage();
plugin.getLocale().getMessage("general.interface.unlocked").getMessage())
.getMessage();
}
public boolean isEnabled(Farm farm) {
@ -178,11 +180,12 @@ public class ModuleAutoCollect extends Module {
}
private boolean useBoneMeal(Farm farm) {
for (ItemStack item : farm.getItems()) {
if (item.getType() != CompatibleMaterial.BONE_MEAL.getMaterial()) continue;
for (ItemStack item : farm.getItems().toArray(new ItemStack[0])) {
if (item == null || item.getType() != CompatibleMaterial.BONE_MEAL.getMaterial()) continue;
farm.removeMaterial(CompatibleMaterial.BONE_MEAL.getMaterial(), 1);
return true;
}
return false;
}
@ -209,8 +212,7 @@ public class ModuleAutoCollect extends Module {
}
private boolean doCropDrop(Farm farm, CompatibleMaterial material) {
if (material == null || farm == null || !material.isCrop()) return false;
if (material == null || farm == null || !material.isCrop() || !plugin.isEnabled()) return false;
BoostData boostData = plugin.getBoostManager().getBoost(farm.getPlacedBy());
@ -224,8 +226,11 @@ public class ModuleAutoCollect extends Module {
Methods.animate(farm.getLocation(), yield);
farm.addItem(stack);
});
if (getCollectionType(farm) != CollectionType.NO_SEEDS)
if (getCollectionType(farm) != CollectionType.NO_SEEDS) {
farm.addItem(seedStack);
}
return true;
}
@ -250,5 +255,4 @@ public class ModuleAutoCollect extends Module {
return EpicFarming.getInstance().getLocale().getMessage("general.interface." + name().replace("_", "").toLowerCase()).getMessage();
}
}
}

View File

@ -2,7 +2,6 @@ package com.songoda.epicfarming.gui;
import com.songoda.core.compatibility.CompatibleMaterial;
import com.songoda.core.gui.CustomizableGui;
import com.songoda.core.gui.Gui;
import com.songoda.core.gui.GuiUtils;
import com.songoda.epicfarming.EpicFarming;
import com.songoda.epicfarming.boost.BoostData;
@ -113,7 +112,7 @@ public class OverviewGui extends CustomizableGui {
farmLore.add(Methods.formatText(line));
}
setItem("farm",13, GuiUtils.createButtonItem(Settings.FARM_BLOCK_MATERIAL.getMaterial(CompatibleMaterial.END_ROD),
setItem("farm", 13, GuiUtils.createButtonItem(Settings.FARM_BLOCK_MATERIAL.getMaterial(CompatibleMaterial.END_ROD),
plugin.getLocale().getMessage("general.nametag.farm")
.processPlaceholder("level", level.getLevel()).getMessage(),
farmLore));
@ -121,17 +120,16 @@ public class OverviewGui extends CustomizableGui {
if (player != null && Settings.UPGRADE_WITH_XP.getBoolean() && player.hasPermission("EpicFarming.Upgrade.XP")) {
setButton("xp", 11, GuiUtils.createButtonItem(Settings.XP_ICON.getMaterial(CompatibleMaterial.EXPERIENCE_BOTTLE),
plugin.getLocale().getMessage("interface.button.upgradewithxp").getMessage(),
nextLevel != null
? plugin.getLocale().getMessage("interface.button.upgradewithxplore")
.processPlaceholder("cost", nextLevel.getCostExperiance()).getMessage()
: plugin.getLocale().getMessage("event.upgrade.maxed").getMessage()),
plugin.getLocale().getMessage("interface.button.upgradewithxp").getMessage(),
nextLevel != null
? plugin.getLocale().getMessage("interface.button.upgradewithxplore")
.processPlaceholder("cost", nextLevel.getCostExperiance()).getMessage()
: plugin.getLocale().getMessage("event.upgrade.maxed").getMessage()),
event -> {
farm.upgrade(UpgradeType.EXPERIENCE, player);
onClose(guiManager, player);
farm.view(player, true);
});
}
if (Settings.UPGRADE_WITH_ECONOMY.getBoolean() && player != null && player.hasPermission("EpicFarming.Upgrade.ECO")) {
@ -146,7 +144,6 @@ public class OverviewGui extends CustomizableGui {
onClose(guiManager, player);
farm.view(player, true);
});
}
Material farmTypeMaterial = CompatibleMaterial.WHEAT.getMaterial();
@ -165,17 +162,16 @@ public class OverviewGui extends CustomizableGui {
farmType.setItemMeta(farmTypeMeta);
Map<Integer, Integer[]> layouts = new HashMap<>();
layouts.put(1, new Integer[]{22});
layouts.put(2, new Integer[]{22, 4});
layouts.put(3, new Integer[]{22, 3, 5});
layouts.put(4, new Integer[]{23, 3, 5, 21});
layouts.put(5, new Integer[]{23, 3, 5, 21, 22});
layouts.put(6, new Integer[]{23, 3, 4, 5, 21, 22});
layouts.put(7, new Integer[]{23, 3, 4, 5, 21, 22, 12});
layouts.put(8, new Integer[]{23, 3, 4, 5, 21, 22, 12, 14});
layouts.put(9, new Integer[]{23, 3, 4, 5, 21, 22, 12, 14, 20});
layouts.put(10, new Integer[]{23, 3, 4, 5, 21, 22, 12, 14, 20, 24});
layouts.put(1, new Integer[] {22});
layouts.put(2, new Integer[] {22, 4});
layouts.put(3, new Integer[] {22, 3, 5});
layouts.put(4, new Integer[] {23, 3, 5, 21});
layouts.put(5, new Integer[] {23, 3, 5, 21, 22});
layouts.put(6, new Integer[] {23, 3, 4, 5, 21, 22});
layouts.put(7, new Integer[] {23, 3, 4, 5, 21, 22, 12});
layouts.put(8, new Integer[] {23, 3, 4, 5, 21, 22, 12, 14});
layouts.put(9, new Integer[] {23, 3, 4, 5, 21, 22, 12, 14, 20});
layouts.put(10, new Integer[] {23, 3, 4, 5, 21, 22, 12, 14, 20, 24});
List<Module> modules = level.getRegisteredModules().stream().filter(module ->
module.getGUIButton(farm) != null).collect(Collectors.toList());
@ -220,12 +216,14 @@ public class OverviewGui extends CustomizableGui {
List<ItemStack> items = farm.getItems();
int j = (page - 1) * 27;
for (int i = 27; i <= 54; i++) {
if (items.size() <= (j))
for (int i = 27; i <= 54; ++i) {
if (items.size() <= j) {
setItem(i, null);
else
} else {
setItem(i, items.get(j));
j++;
}
++j;
}
}
@ -237,11 +235,13 @@ public class OverviewGui extends CustomizableGui {
if (i >= start && i < start + 27) {
ItemStack item = getItem(j);
j++;
if (item != null && item.getType() != Material.AIR)
if (item != null && item.getType() != Material.AIR) {
items.add(item);
}
} else {
if (i >= farm.getItems().size())
if (i >= farm.getItems().size()) {
continue;
}
items.add(farm.getItems().get(i));
}
}

View File

@ -5,7 +5,6 @@ import com.songoda.epicfarming.EpicFarming;
import com.songoda.epicfarming.farming.Farm;
import com.songoda.epicfarming.farming.FarmType;
import com.songoda.epicfarming.settings.Settings;
import com.songoda.epicfarming.tasks.FarmTask;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
@ -114,7 +113,7 @@ public class BlockListeners implements Listener {
plugin.getDataManager().deleteFarm(farm);
farm.forceMenuClose();
FarmTask.getCrops(farm, false);
plugin.getFarmTask().getCrops(farm, false);
event.setCancelled(true);
@ -125,7 +124,7 @@ public class BlockListeners implements Listener {
block.setType(Material.AIR);
block.getLocation().getWorld().dropItemNaturally(block.getLocation().add(.5, .5, .5), item);
for (ItemStack itemStack : farm.getItems()) {
for (ItemStack itemStack : farm.getItems().toArray(new ItemStack[0])) {
farm.getLocation().getWorld().dropItemNaturally(farm.getLocation().add(.5, .5, .5), itemStack);
}
}
@ -138,7 +137,7 @@ public class BlockListeners implements Listener {
Farm farm = plugin.getFarmManager().removeFarm(event.getBlock().getLocation());
if (farm == null) return;
FarmTask.getCrops(farm, false);
plugin.getFarmTask().getCrops(farm, false);
plugin.getDataManager().deleteFarm(farm);
farm.forceMenuClose();
@ -152,7 +151,7 @@ public class BlockListeners implements Listener {
block.setType(Material.AIR);
block.getLocation().getWorld().dropItemNaturally(block.getLocation().add(.5, .5, .5), item);
for (ItemStack itemStack : farm.getItems()) {
for (ItemStack itemStack : farm.getItems().toArray(new ItemStack[0])) {
farm.getLocation().getWorld().dropItemNaturally(farm.getLocation().add(.5, .5, .5), itemStack);
}
}
@ -164,4 +163,4 @@ public class BlockListeners implements Listener {
event.setCancelled(true);
}
}
}
}

View File

@ -6,7 +6,6 @@ import com.songoda.epicfarming.farming.Farm;
import com.songoda.epicfarming.farming.levels.modules.Module;
import com.songoda.epicfarming.farming.levels.modules.ModuleAutoCollect;
import com.songoda.epicfarming.settings.Settings;
import com.songoda.epicfarming.tasks.FarmTask;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
@ -112,14 +111,14 @@ public class EntityListeners implements Listener {
plugin.getDataManager().deleteFarm(farm);
farm.forceMenuClose();
FarmTask.getCrops(farm, false);
plugin.getFarmTask().getCrops(farm, false);
ItemStack item = plugin.makeFarmItem(farm.getLevel());
block.setType(Material.AIR);
block.getLocation().getWorld().dropItemNaturally(block.getLocation().add(.5, .5, .5), item);
for (ItemStack itemStack : farm.getItems()) {
for (ItemStack itemStack : farm.getItems().toArray(new ItemStack[0])) {
farm.getLocation().getWorld().dropItemNaturally(farm.getLocation().add(.5, .5, .5), itemStack);
}
}

View File

@ -59,5 +59,4 @@ public abstract class Storage {
public abstract void makeBackup();
public abstract void closeConnection();
}

View File

@ -25,24 +25,19 @@ import java.util.UUID;
import java.util.stream.Collectors;
public class FarmTask extends BukkitRunnable {
private final EpicFarming plugin;
private static FarmTask instance;
private static EpicFarming plugin;
private final Map<UUID, Collection<LivingEntity>> entityCache = new HashMap<>();
private static Map<UUID, Collection<LivingEntity>> entityCache = new HashMap<>();
public FarmTask(EpicFarming plugin) {
this.plugin = plugin;
public static FarmTask startTask(EpicFarming pl) {
if (instance != null) {
instance.cancel();
}
instance = new FarmTask();
instance.runTaskTimerAsynchronously(plugin = pl, 0, Settings.FARM_TICK_SPEED.getInt());
return instance;
runTaskTimerAsynchronously(this.plugin, 0, Settings.FARM_TICK_SPEED.getInt());
}
public static List<Block> getCrops(Farm farm, boolean add) {
public List<Block> getCrops(Farm farm, boolean add) {
if (((System.currentTimeMillis() - farm.getLastCached()) > (30 * 1000)) || !add) {
Bukkit.getScheduler().runTask(plugin, () -> {
Bukkit.getScheduler().runTask(this.plugin, () -> {
farm.setLastCached(System.currentTimeMillis());
if (add) farm.clearCache();
Block block = farm.getLocation().getBlock();
@ -67,18 +62,25 @@ public class FarmTask extends BukkitRunnable {
continue;
}
farm.removeCachedCrop(b2);
plugin.getGrowthTask().removeCropByLocation(b2.getLocation());
this.plugin.getGrowthTask().removeCropByLocation(b2.getLocation());
}
}
}
});
}
return farm.getCachedCrops();
}
@Override
public void run() {
GrowthTask growthTask = plugin.getGrowthTask();
if (growthTask.isCancelled()) return;
for (Farm farm : new ArrayList<>(plugin.getFarmManager().getFarms().values())) {
if (!plugin.isEnabled()) return; // Prevent registering a task on plugin disable
try {
if (!farm.isInLoadedChunk()) continue;
@ -93,7 +95,6 @@ public class FarmTask extends BukkitRunnable {
.collect(Collectors.toCollection(ArrayList::new)));
});
Collection<LivingEntity> entitiesAroundFarm = entityCache.get(farm.getUniqueId());
if (entitiesAroundFarm == null) continue;
@ -101,11 +102,12 @@ public class FarmTask extends BukkitRunnable {
List<Block> crops = getCrops(farm, true);
if (farm.getFarmType() != FarmType.LIVESTOCK)
for (Block block : crops)
for (Block block : crops) {
if (!BlockUtils.isCropFullyGrown(block)) {
// Add to GrowthTask
plugin.getGrowthTask().addLiveCrop(block.getLocation(), new Crop(block.getLocation(), farm));
growthTask.addLiveCrop(block.getLocation(), new Crop(block.getLocation(), farm));
}
}
// Cycle through modules.
farm.getLevel().getRegisteredModules().stream()
@ -114,11 +116,11 @@ public class FarmTask extends BukkitRunnable {
// Run Module
module.run(farm, entitiesAroundFarm, crops);
});
} catch (Exception e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
Bukkit.getScheduler().runTask(plugin, () ->
plugin.getEntityUtils().clearChunkCache());
Bukkit.getScheduler().runTask(plugin, () -> plugin.getEntityUtils().clearChunkCache());
}
}
}

View File

@ -16,20 +16,12 @@ import java.util.Map;
import java.util.Random;
public class GrowthTask extends BukkitRunnable {
private static GrowthTask instance;
private final Map<Location, Crop> liveCrops = new HashMap<>();
private static final Random random = new Random();
public static GrowthTask startTask(EpicFarming plugin) {
if (instance != null) {
instance.cancel();
}
instance = new GrowthTask();
instance.runTaskTimer(plugin, 0, Settings.GROWTH_TICK_SPEED.getInt());
return instance;
public GrowthTask(EpicFarming plugin) {
runTaskTimer(plugin, 0, Settings.GROWTH_TICK_SPEED.getInt());
}
@Override
@ -64,7 +56,6 @@ public class GrowthTask extends BukkitRunnable {
liveCrops.remove(crop.getLocation());
}
public synchronized void addLiveCrop(Location location, Crop crop) {
if (!liveCrops.containsKey(location))
liveCrops.put(location, crop);
@ -73,5 +64,4 @@ public class GrowthTask extends BukkitRunnable {
public synchronized void removeCropByLocation(Location location) {
liveCrops.remove(location);
}
}
}

View File

@ -22,7 +22,6 @@ public class HopperTask extends BukkitRunnable {
this.manager = plugin.getFarmManager();
}
public static HopperTask startTask(EpicFarming plugin) {
if (instance != null) {
instance.cancel();
@ -56,7 +55,7 @@ public class HopperTask extends BukkitRunnable {
Inventory hopperInventory = ((Hopper) block.getState()).getInventory();
for (ItemStack item : farm.getItems()) {
for (ItemStack item : farm.getItems().toArray(new ItemStack[0])) {
if (item.getType() == CompatibleMaterial.BONE_MEAL.getMaterial()) continue;
ItemStack toMove = item.clone();
@ -80,4 +79,4 @@ public class HopperTask extends BukkitRunnable {
}
return false;
}
}
}