UltimateStacker/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/UltimateStacker.java

463 lines
18 KiB
Java
Raw Normal View History

2023-05-25 19:20:03 +02:00
package com.craftaro.ultimatestacker;
import com.craftaro.core.SongodaCore;
import com.craftaro.core.SongodaPlugin;
import com.craftaro.core.commands.CommandManager;
import com.craftaro.core.compatibility.ServerVersion;
import com.craftaro.core.configuration.Config;
import com.craftaro.core.database.DataManager;
2024-01-07 12:16:27 +01:00
import com.craftaro.core.dependency.Dependency;
import com.craftaro.core.gui.GuiManager;
import com.craftaro.core.hooks.EntityStackerManager;
import com.craftaro.core.hooks.HologramManager;
import com.craftaro.core.hooks.ProtectionManager;
import com.craftaro.core.hooks.WorldGuardHook;
import com.craftaro.core.utils.TextUtils;
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
import com.craftaro.ultimatestacker.api.UltimateStackerApi;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.api.stack.block.BlockStack;
import com.craftaro.ultimatestacker.api.stack.block.BlockStackManager;
import com.craftaro.ultimatestacker.api.stack.entity.EntityStack;
import com.craftaro.ultimatestacker.api.stack.entity.EntityStackManager;
2023-05-30 11:20:31 +02:00
import com.craftaro.ultimatestacker.api.stack.item.StackedItemManager;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.api.stack.spawner.SpawnerStack;
import com.craftaro.ultimatestacker.api.stack.spawner.SpawnerStackManager;
2023-05-30 11:21:46 +02:00
import com.craftaro.ultimatestacker.api.utils.Hologramable;
import com.craftaro.ultimatestacker.commands.*;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.database.migrations._1_InitialMigration;
import com.craftaro.ultimatestacker.database.migrations._2_EntityStacks;
import com.craftaro.ultimatestacker.database.migrations._3_BlockStacks;
import com.craftaro.ultimatestacker.database.migrations._6_RemoveStackedEntityTable;
2023-05-30 11:21:46 +02:00
import com.craftaro.ultimatestacker.hook.StackerHook;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.hook.hooks.JobsHook;
import com.craftaro.ultimatestacker.hook.hooks.SuperiorSkyblock2Hook;
import com.craftaro.ultimatestacker.listeners.*;
2023-05-30 11:21:46 +02:00
import com.craftaro.ultimatestacker.listeners.entity.EntityCurrentListener;
import com.craftaro.ultimatestacker.listeners.entity.EntityListeners;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.listeners.item.ItemCurrentListener;
import com.craftaro.ultimatestacker.listeners.item.ItemLegacyListener;
import com.craftaro.ultimatestacker.listeners.item.ItemListeners;
import com.craftaro.ultimatestacker.lootables.LootablesManager;
2023-05-30 11:21:46 +02:00
import com.craftaro.ultimatestacker.settings.Settings;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.stackable.block.BlockStackImpl;
2023-05-30 11:21:46 +02:00
import com.craftaro.ultimatestacker.stackable.block.BlockStackManagerImpl;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.stackable.entity.EntityStackManagerImpl;
import com.craftaro.ultimatestacker.stackable.entity.custom.CustomEntityManager;
2023-05-30 11:20:31 +02:00
import com.craftaro.ultimatestacker.stackable.item.StackedItemManagerImpl;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.stackable.spawner.SpawnerStackImpl;
2023-05-30 11:21:46 +02:00
import com.craftaro.ultimatestacker.stackable.spawner.SpawnerStackManagerImpl;
import com.craftaro.ultimatestacker.tasks.BreedingTask;
2023-05-30 11:21:46 +02:00
import com.craftaro.ultimatestacker.tasks.StackingTask;
2023-05-25 19:20:03 +02:00
import com.craftaro.ultimatestacker.utils.Async;
2019-12-27 18:42:02 +01:00
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
2018-11-06 04:33:10 +01:00
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
2019-09-03 22:38:00 +02:00
import org.bukkit.inventory.ItemStack;
2019-01-23 19:01:31 +01:00
import org.bukkit.plugin.PluginManager;
2018-11-06 04:33:10 +01:00
import java.util.*;
2020-04-26 14:42:18 +02:00
2019-09-03 22:38:00 +02:00
public class UltimateStacker extends SongodaPlugin {
2018-11-06 04:33:10 +01:00
private static UltimateStacker INSTANCE;
private final static Set<String> whitelist = new HashSet();
2020-04-26 14:42:18 +02:00
private final static Set<String> blacklist = new HashSet();
2018-11-06 04:33:10 +01:00
2019-09-03 22:38:00 +02:00
private final Config mobFile = new Config(this, "mobs.yml");
private final Config itemFile = new Config(this, "items.yml");
private final Config spawnerFile = new Config(this, "spawners.yml");
2018-11-06 04:33:10 +01:00
2019-09-03 22:38:00 +02:00
private final GuiManager guiManager = new GuiManager(this);
private final List<StackerHook> stackerHooks = new ArrayList<>();
2018-11-06 04:33:10 +01:00
private EntityStackManager entityStackManager;
private SpawnerStackManager spawnerStackManager;
2020-08-25 01:01:11 +02:00
private BlockStackManager blockStackManager;
2023-05-30 11:20:31 +02:00
private StackedItemManager stackedItemManager;
2019-07-23 16:55:49 +02:00
private LootablesManager lootablesManager;
2018-11-06 04:33:10 +01:00
private CommandManager commandManager;
private CustomEntityManager customEntityManager;
2018-11-06 04:33:10 +01:00
private StackingTask stackingTask;
private BreedingTask breedingTask;
private UltimateStackerApi API;
private SuperiorSkyblock2Hook superiorSkyblock2Hook;
private boolean instantStacking;
2019-08-02 15:59:10 +02:00
2018-11-06 06:09:40 +01:00
public static UltimateStacker getInstance() {
return INSTANCE;
}
2024-01-07 12:16:27 +01:00
@Override
protected Set<Dependency> getDependencies() {
return new HashSet<>();
2024-01-07 12:16:27 +01:00
}
2019-09-03 22:38:00 +02:00
@Override
public void onPluginLoad() {
INSTANCE = this;
2019-09-18 18:02:35 +02:00
// Register WorldGuard
WorldGuardHook.addHook("mob-stacking", true);
2019-09-03 22:38:00 +02:00
}
2020-04-26 14:42:18 +02:00
2019-09-03 22:38:00 +02:00
@Override
public void onPluginDisable() {
if (this.stackingTask != null)
this.stackingTask.cancel();
this.dataManager.saveBatchSync(this.spawnerStackManager.getStacksData());
this.dataManager.saveBatchSync(this.blockStackManager.getStacksData());
this.dataManager.shutdownNow();
2019-09-03 22:38:00 +02:00
HologramManager.removeAllHolograms();
Async.shutdown();
2018-11-06 04:33:10 +01:00
}
2018-11-09 05:11:49 +01:00
@Override
2019-09-03 22:38:00 +02:00
public void onPluginEnable() {
// Run Songoda Updater
2023-01-25 15:50:41 +01:00
Async.start();
2023-06-29 11:18:18 +02:00
SongodaCore.registerPlugin(this, 16, XMaterial.IRON_INGOT);
2019-09-03 22:38:00 +02:00
// Setup Config
2019-09-07 23:55:16 +02:00
Settings.setupConfig();
2020-04-26 14:42:18 +02:00
this.setLocale(Settings.LANGUGE_MODE.getString(), false);
blacklist.clear();
whitelist.clear();
whitelist.addAll(Settings.ITEM_WHITELIST.getStringList());
blacklist.addAll(Settings.ITEM_BLACKLIST.getStringList());
2020-04-26 14:42:18 +02:00
2019-09-03 22:38:00 +02:00
// Setup plugin commands
this.commandManager = new CommandManager(this);
2020-06-28 04:12:13 +02:00
this.commandManager.addMainCommand("us")
2020-09-01 20:30:08 +02:00
.addSubCommands(new CommandSettings(this, guiManager),
new CommandRemoveAll(this),
new CommandReload(this),
new CommandGiveSpawner(this),
new CommandSpawn(this),
new CommandLootables(this),
new CommandConvert(guiManager)
2020-06-28 04:12:13 +02:00
);
2018-11-06 04:33:10 +01:00
PluginManager pluginManager = Bukkit.getPluginManager();
this.superiorSkyblock2Hook = new SuperiorSkyblock2Hook(pluginManager.isPluginEnabled("SuperiorSkyblock2"));
this.lootablesManager = new LootablesManager(superiorSkyblock2Hook);
2019-07-23 16:55:49 +02:00
this.lootablesManager.createDefaultLootables();
this.getLootablesManager().getLootManager().loadLootables();
2019-07-08 18:39:57 +02:00
2018-11-06 04:33:10 +01:00
for (EntityType value : EntityType.values()) {
2018-11-06 05:53:27 +01:00
if (value.isSpawnable() && value.isAlive() && !value.toString().contains("ARMOR")) {
2019-09-03 22:38:00 +02:00
mobFile.addDefault("Mobs." + value.name() + ".Enabled", true);
2021-03-05 15:09:00 +01:00
mobFile.addDefault("Mobs." + value.name() + ".Display Name", TextUtils.formatText(value.name().toLowerCase().replace("_", " "), true));
2019-09-03 22:38:00 +02:00
mobFile.addDefault("Mobs." + value.name() + ".Max Stack Size", -1);
mobFile.addDefault("Mobs." + value.name() + ".Kill Whole Stack", false);
2018-11-06 04:33:10 +01:00
}
}
2019-09-03 22:38:00 +02:00
mobFile.load();
mobFile.saveChanges();
2018-11-06 04:33:10 +01:00
for (Material value : Material.values()) {
2019-09-03 22:38:00 +02:00
itemFile.addDefault("Items." + value.name() + ".Has Hologram", true);
itemFile.addDefault("Items." + value.name() + ".Max Stack Size", -1);
2019-12-27 18:42:02 +01:00
itemFile.addDefault("Items." + value.name() + ".Display Name", WordUtils.capitalizeFully(value.name().toLowerCase().replace("_", " ")));
2018-11-06 04:33:10 +01:00
}
2019-09-03 22:38:00 +02:00
itemFile.load();
itemFile.saveChanges();
2018-11-06 04:33:10 +01:00
for (EntityType value : EntityType.values()) {
2018-11-06 05:53:27 +01:00
if (value.isSpawnable() && value.isAlive() && !value.toString().contains("ARMOR")) {
2019-09-03 22:38:00 +02:00
spawnerFile.addDefault("Spawners." + value.name() + ".Max Stack Size", -1);
2021-03-05 15:09:00 +01:00
spawnerFile.addDefault("Spawners." + value.name() + ".Display Name", TextUtils.formatText(value.name().toLowerCase().replace("_", " "), true));
2018-11-06 04:33:10 +01:00
}
}
2019-09-03 22:38:00 +02:00
spawnerFile.load();
spawnerFile.saveChanges();
2018-11-06 04:33:10 +01:00
2023-04-01 15:55:37 +02:00
if (Bukkit.getPluginManager().isPluginEnabled("BentoBox")) {
ProtectionManager.load(Bukkit.getPluginManager().getPlugin("BentoBox"));
}
2023-05-25 19:20:03 +02:00
this.spawnerStackManager = new SpawnerStackManagerImpl();
this.entityStackManager = new EntityStackManagerImpl(this);
this.blockStackManager = new BlockStackManagerImpl();
2023-05-30 11:20:31 +02:00
this.stackedItemManager = new StackedItemManagerImpl();
this.customEntityManager = new CustomEntityManager();
2018-11-06 04:33:10 +01:00
2019-09-03 22:38:00 +02:00
guiManager.init();
2019-09-03 22:38:00 +02:00
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_10))
pluginManager.registerEvents(new BreedListeners(this), this);
pluginManager.registerEvents(new BlockListeners(this), this);
pluginManager.registerEvents(new DeathListeners(this), this);
pluginManager.registerEvents(new ShearListeners(this), this);
pluginManager.registerEvents(new InteractListeners(this), this);
2020-09-09 16:12:32 +02:00
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13))
pluginManager.registerEvents(new EntityCurrentListener(this), this);
this.instantStacking = Settings.STACK_ENTITIES.getBoolean() && Settings.INSTANT_STACKING.getBoolean();
pluginManager.registerEvents(new EntityListeners(this), this);
pluginManager.registerEvents(new ItemListeners(this), this);
2023-05-30 11:20:31 +02:00
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_12)) {
pluginManager.registerEvents(new ItemCurrentListener(), this);
2023-05-30 11:20:31 +02:00
} else {
pluginManager.registerEvents(new ItemLegacyListener(), this);
2023-05-30 11:20:31 +02:00
}
pluginManager.registerEvents(new TameListeners(this), this);
pluginManager.registerEvents(new SpawnerListeners(this), this);
pluginManager.registerEvents(new SheepDyeListeners(this), this);
2018-11-06 04:33:10 +01:00
2023-05-30 11:20:31 +02:00
if (Settings.CLEAR_LAG.getBoolean() && pluginManager.isPluginEnabled("ClearLag")) {
2019-06-20 09:33:29 +02:00
pluginManager.registerEvents(new ClearLagListeners(this), this);
2023-05-30 11:20:31 +02:00
}
2019-06-20 09:33:29 +02:00
// Register Hooks
2023-05-30 11:20:31 +02:00
if (pluginManager.isPluginEnabled("Jobs")) {
stackerHooks.add(new JobsHook());
2023-05-30 11:20:31 +02:00
}
2021-02-22 17:17:18 +01:00
2019-09-04 15:17:45 +02:00
HologramManager.load(this);
EntityStackerManager.load();
initDatabase(Arrays.asList(new _1_InitialMigration(), new _2_EntityStacks(), new _3_BlockStacks(), new _6_RemoveStackedEntityTable()));
2023-05-25 19:20:03 +02:00
API = new UltimateStackerApi(this, entityStackManager, stackedItemManager, spawnerStackManager, blockStackManager, new Settings());
2020-08-25 01:01:11 +02:00
}
2019-08-02 15:59:10 +02:00
2020-08-25 01:01:11 +02:00
@Override
public void onDataLoad() {
2020-11-30 20:56:38 +01:00
if (HologramManager.isEnabled())
// Set the offset so that the holograms don't end up inside the blocks.
HologramManager.getHolograms().setPositionOffset(.5, .65, .5);
2020-11-30 20:56:38 +01:00
2020-08-25 01:01:11 +02:00
// Load current data.
final boolean useSpawnerHolo = Settings.SPAWNER_HOLOGRAMS.getBoolean();
this.dataManager.loadBatch(SpawnerStackImpl.class, "spawners").forEach((data) -> {
SpawnerStack spawner = (SpawnerStack) data;
this.spawnerStackManager.addSpawner(spawner);
2020-08-25 01:01:11 +02:00
if (useSpawnerHolo) {
if (spawner == null) return;
if (spawner.getLocation() == null) return;
if (spawner.getLocation().getWorld() != null) {
updateHologram(spawner);
2020-08-25 01:01:11 +02:00
}
}
});
//Start stacking task
if (Settings.STACK_ENTITIES.getBoolean()) {
this.breedingTask = new BreedingTask(this);
this.stackingTask = new StackingTask(this);
}
this.instantStacking = Settings.STACK_ENTITIES.getBoolean() && Settings.INSTANT_STACKING.getBoolean();
final boolean useBlockHolo = Settings.BLOCK_HOLOGRAMS.getBoolean();
this.dataManager.loadBatch(BlockStackImpl.class, "blocks").forEach((data) -> {
BlockStack blockStack = (BlockStack) data;
this.blockStackManager.addBlock(blockStack);
2020-08-25 01:01:11 +02:00
if (useBlockHolo) {
if (blockStack == null) return;
if (blockStack.getLocation().getWorld() != null)
updateHologram(blockStack);
2020-08-25 01:01:11 +02:00
}
});
2018-11-06 04:33:10 +01:00
}
public UltimateStackerApi getAPI() {
2023-05-25 19:20:03 +02:00
return API;
}
public void addExp(Player player, EntityStack stack) {
for (StackerHook stackerHook : stackerHooks) {
stackerHook.applyExperience(player, stack);
}
}
2019-09-03 22:38:00 +02:00
@Override
public List<Config> getExtraConfig() {
return Arrays.asList(mobFile, itemFile, spawnerFile);
}
2020-04-26 14:42:18 +02:00
2019-09-03 22:38:00 +02:00
@Override
public void onConfigReload() {
blacklist.clear();
whitelist.clear();
whitelist.addAll(Settings.ITEM_WHITELIST.getStringList());
blacklist.addAll(Settings.ITEM_BLACKLIST.getStringList());
2020-04-26 14:42:18 +02:00
this.setLocale(getConfig().getString("System.Language Mode"), true);
2018-11-06 04:33:10 +01:00
this.locale.reloadMessages();
2019-07-31 06:29:10 +02:00
if (stackingTask != null)
this.stackingTask.cancel();
if (Settings.STACK_ENTITIES.getBoolean()) {
this.stackingTask = new StackingTask(this);
}
2019-07-31 06:29:10 +02:00
2019-09-03 22:38:00 +02:00
this.mobFile.load();
this.itemFile.load();
this.spawnerFile.load();
2019-07-23 16:55:49 +02:00
this.getLootablesManager().getLootManager().loadLootables();
2018-11-06 04:33:10 +01:00
}
public boolean spawnersEnabled() {
2020-08-25 01:01:11 +02:00
return !this.getServer().getPluginManager().isPluginEnabled("EpicSpawners")
&& Settings.SPAWNERS_ENABLED.getBoolean();
}
2018-11-06 04:33:10 +01:00
public CommandManager getCommandManager() {
return commandManager;
}
2019-07-23 16:55:49 +02:00
public LootablesManager getLootablesManager() {
return lootablesManager;
2019-07-08 18:39:57 +02:00
}
2018-11-06 04:33:10 +01:00
public StackingTask getStackingTask() {
return stackingTask;
}
2019-09-03 22:38:00 +02:00
public Config getMobFile() {
2018-11-06 04:33:10 +01:00
return mobFile;
}
2019-09-03 22:38:00 +02:00
public Config getItemFile() {
2018-11-06 04:33:10 +01:00
return itemFile;
}
2019-09-03 22:38:00 +02:00
public Config getSpawnerFile() {
2018-11-06 05:53:27 +01:00
return spawnerFile;
}
2019-07-31 06:29:10 +02:00
public DataManager getPluginDataManager() {
2019-08-02 15:59:10 +02:00
return dataManager;
}
2019-09-03 22:38:00 +02:00
2020-06-03 15:44:12 +02:00
public GuiManager getGuiManager() {
return guiManager;
}
2020-08-25 01:01:11 +02:00
public BlockStackManager getBlockStackManager() {
return blockStackManager;
2019-09-03 22:38:00 +02:00
}
2023-05-25 19:20:03 +02:00
public EntityStackManager getEntityStackManager() {
return entityStackManager;
}
2023-05-30 11:20:31 +02:00
public StackedItemManager getStackedItemManager() {
return stackedItemManager;
2023-05-25 19:20:03 +02:00
}
public SpawnerStackManager getSpawnerStackManager() {
return spawnerStackManager;
}
public CustomEntityManager getCustomEntityManager() {
return customEntityManager;
}
2019-09-03 22:38:00 +02:00
2020-08-25 01:01:11 +02:00
public void updateHologram(Hologramable stack) {
2020-08-26 15:40:51 +02:00
// Is this stack invalid?
2023-09-13 13:52:04 +02:00
if (!stack.isValid()) {
if (stack instanceof BlockStackImpl) {
2020-08-26 15:40:51 +02:00
blockStackManager.removeBlock(stack.getLocation());
2023-09-18 15:34:19 +02:00
BlockStackImpl blockStack = (BlockStackImpl) stack;
dataManager.delete(blockStack);
2023-09-13 13:52:04 +02:00
} else if (stack instanceof SpawnerStackImpl) {
2020-08-26 15:40:51 +02:00
spawnerStackManager.removeSpawner(stack.getLocation());
2023-09-18 15:34:19 +02:00
SpawnerStackImpl spawnerStack = (SpawnerStackImpl) stack;
dataManager.delete(spawnerStack);
2023-09-13 13:52:04 +02:00
}
2023-09-18 15:34:19 +02:00
} else {
// are holograms enabled?
if (!stack.areHologramsEnabled() && !HologramManager.getManager().isEnabled()) return;
// update the hologram
if (stack.getHologramName() == null) {
if (stack instanceof BlockStackImpl) {
BlockStackImpl blockStack = (BlockStackImpl) stack;
getLogger().warning("Hologram name is null for BlocStack at " + blockStack.getLocation());
} else {
SpawnerStackImpl spawnerStack = (SpawnerStackImpl) stack;
getLogger().warning("Hologram name is null for SpawnerStack at " + spawnerStack.getLocation());
}
return;
}
if (!HologramManager.isHologramLoaded(stack.getHologramId())) {
HologramManager.createHologram(stack.getHologramId(), stack.getLocation(), stack.getHologramName());
return;
}
HologramManager.updateHologram(stack.getHologramId(), stack.getHologramName());
2021-12-22 23:11:16 +01:00
}
2019-09-03 22:38:00 +02:00
}
2020-08-25 01:01:11 +02:00
public void removeHologram(Hologramable stack) {
2021-12-22 23:11:16 +01:00
HologramManager.removeHologram(stack.getHologramId());
2019-09-03 22:38:00 +02:00
}
public SuperiorSkyblock2Hook getSuperiorSkyblock2Hook() {
return superiorSkyblock2Hook;
}
public boolean isInstantStacking() {
return instantStacking;
}
2019-09-03 22:38:00 +02:00
//////// Convenient API //////////
2020-04-26 14:42:18 +02:00
/**
* Check to see if this material is not permitted to stack
*
* @param item Item material to check
* @return true if this material will not stack
*/
public static boolean isMaterialBlacklisted(ItemStack item) {
2023-06-29 11:18:18 +02:00
Optional<XMaterial> mat = XMaterial.matchXMaterial(item.getType().name());
// this shouldn't happen, but just in case?
return mat.map(xMaterial -> isMaterialBlacklisted(xMaterial.name()) || isMaterialBlacklisted(xMaterial.parseMaterial()))
.orElseGet(() -> ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) ?
isMaterialBlacklisted(item.getType()) : isMaterialBlacklisted(item.getType(), item.getData().getData()));
}
2019-09-03 22:38:00 +02:00
/**
* Check to see if this material is not permitted to stack
*
* @param type Material to check
* @return true if this material will not stack
*/
public static boolean isMaterialBlacklisted(String type) {
return !whitelist.isEmpty() && !whitelist.contains(type)
|| !blacklist.isEmpty() && blacklist.contains(type);
2019-09-03 22:38:00 +02:00
}
/**
* Check to see if this material is not permitted to stack
*
* @param type Material to check
2019-09-03 22:38:00 +02:00
* @return true if this material will not stack
*/
public static boolean isMaterialBlacklisted(Material type) {
return !whitelist.isEmpty() && !whitelist.contains(type.name())
|| !blacklist.isEmpty() && blacklist.contains(type.name());
2019-09-03 22:38:00 +02:00
}
/**
* Check to see if this material is not permitted to stack
*
* @param type Material to check
* @param data data value for this item (for 1.12 and older servers)
2019-09-03 22:38:00 +02:00
* @return true if this material will not stack
*/
public static boolean isMaterialBlacklisted(Material type, byte data) {
String combined = type.toString() + ":" + data;
return !whitelist.isEmpty() && !whitelist.contains(combined)
|| !blacklist.isEmpty() && blacklist.contains(combined);
}
public BreedingTask getBreedingTask() {
return breedingTask;
}
2018-11-06 04:33:10 +01:00
}