2017-12-29 05:39:07 +01:00
|
|
|
package bskyblock.addon.challenges;
|
2017-11-26 03:17:16 +01:00
|
|
|
|
|
|
|
import java.beans.IntrospectionException;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import java.sql.SQLException;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
2017-11-27 08:13:17 +01:00
|
|
|
import java.util.LinkedHashMap;
|
2017-11-26 03:17:16 +01:00
|
|
|
import java.util.List;
|
2017-11-27 08:13:17 +01:00
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Map.Entry;
|
|
|
|
import java.util.stream.Collectors;
|
2017-11-26 03:17:16 +01:00
|
|
|
|
2018-02-23 08:52:39 +01:00
|
|
|
import org.bukkit.Bukkit;
|
2017-11-26 03:17:16 +01:00
|
|
|
import org.bukkit.ChatColor;
|
|
|
|
import org.bukkit.Material;
|
2018-02-20 04:50:34 +01:00
|
|
|
import org.bukkit.inventory.Inventory;
|
2017-11-26 03:17:16 +01:00
|
|
|
import org.bukkit.inventory.ItemStack;
|
|
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
|
|
|
|
2018-02-20 04:50:34 +01:00
|
|
|
import bskyblock.addon.challenges.database.object.ChallengesData;
|
|
|
|
import bskyblock.addon.challenges.database.object.ChallengesData.ChallengeType;
|
2017-12-29 05:39:07 +01:00
|
|
|
import bskyblock.addon.challenges.database.object.LevelsDO;
|
|
|
|
import bskyblock.addon.challenges.panel.ChallengesPanels;
|
|
|
|
import us.tastybento.bskyblock.BSkyBlock;
|
|
|
|
import us.tastybento.bskyblock.api.commands.User;
|
2017-11-26 03:17:16 +01:00
|
|
|
import us.tastybento.bskyblock.database.flatfile.FlatFileDatabase;
|
|
|
|
import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler;
|
|
|
|
|
2017-11-27 08:13:17 +01:00
|
|
|
public class ChallengesManager {
|
2017-11-26 03:17:16 +01:00
|
|
|
|
2017-11-27 08:13:17 +01:00
|
|
|
//private static final boolean DEBUG = false;
|
2018-02-20 04:50:34 +01:00
|
|
|
private Challenges addon;
|
|
|
|
private LinkedHashMap<LevelsDO, List<ChallengesData>> challengeList;
|
2017-11-26 03:17:16 +01:00
|
|
|
|
2018-02-20 04:50:34 +01:00
|
|
|
private AbstractDatabaseHandler<ChallengesData> chHandler;
|
2017-11-26 03:17:16 +01:00
|
|
|
private AbstractDatabaseHandler<LevelsDO> lvHandler;
|
2017-11-27 08:13:17 +01:00
|
|
|
|
|
|
|
private ChallengesPanels challengesPanels;
|
2017-11-26 03:17:16 +01:00
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
2017-11-27 08:13:17 +01:00
|
|
|
public ChallengesManager(Challenges plugin) {
|
2018-02-20 04:50:34 +01:00
|
|
|
this.addon = plugin;
|
2017-11-26 03:17:16 +01:00
|
|
|
// Set up the database handler to store and retrieve Challenges
|
2018-02-20 04:50:34 +01:00
|
|
|
chHandler = (AbstractDatabaseHandler<ChallengesData>) new FlatFileDatabase().getHandler(ChallengesData.class);
|
|
|
|
lvHandler = (AbstractDatabaseHandler<LevelsDO>) new FlatFileDatabase().getHandler(LevelsDO.class);
|
2017-11-27 08:13:17 +01:00
|
|
|
challengeList = new LinkedHashMap<>();
|
|
|
|
// Start panels
|
|
|
|
challengesPanels = new ChallengesPanels(plugin, this);
|
2017-11-26 03:17:16 +01:00
|
|
|
load();
|
|
|
|
}
|
|
|
|
|
2017-11-27 08:13:17 +01:00
|
|
|
/**
|
|
|
|
* @return the challengesPanels
|
|
|
|
*/
|
|
|
|
public ChallengesPanels getChallengesPanels() {
|
|
|
|
return challengesPanels;
|
|
|
|
}
|
|
|
|
|
2018-02-20 04:50:34 +01:00
|
|
|
public AbstractDatabaseHandler<ChallengesData> getHandler() {
|
2017-11-26 03:17:16 +01:00
|
|
|
return chHandler;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear and reload all challenges
|
|
|
|
*/
|
|
|
|
public void load() {
|
2017-11-27 08:13:17 +01:00
|
|
|
// Load the challenges
|
|
|
|
challengeList.clear();
|
2017-11-26 03:17:16 +01:00
|
|
|
try {
|
2018-02-20 04:50:34 +01:00
|
|
|
for (ChallengesData challenge : chHandler.loadObjects()) {
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: Loading challenge " + challenge.getFriendlyName() + " level " + challenge.getLevel());
|
2017-11-27 08:13:17 +01:00
|
|
|
// See if we have this level already
|
2017-12-29 05:39:07 +01:00
|
|
|
LevelsDO level;
|
2018-02-20 04:50:34 +01:00
|
|
|
if (lvHandler.objectExists(challenge.getLevel())) {
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: Level contains level " + challenge.getLevel());
|
2017-11-27 08:13:17 +01:00
|
|
|
// Get it from the database
|
|
|
|
level = lvHandler.loadObject(challenge.getLevel());
|
|
|
|
} else {
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: Level does not contains level " + challenge.getLevel());
|
2017-11-27 08:13:17 +01:00
|
|
|
// Make it
|
2017-12-29 05:39:07 +01:00
|
|
|
level = new LevelsDO();
|
2017-11-27 08:13:17 +01:00
|
|
|
level.setUniqueId(challenge.getLevel());
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: Level unique Id set to " + level.getUniqueId());
|
2017-11-27 08:13:17 +01:00
|
|
|
lvHandler.saveObject(level);
|
|
|
|
}
|
|
|
|
if (challengeList.containsKey(level)) {
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: Challenge contains level " + level.getUniqueId());
|
2017-11-27 08:13:17 +01:00
|
|
|
challengeList.get(level).add(challenge);
|
|
|
|
} else {
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: No key found");
|
2017-11-27 08:13:17 +01:00
|
|
|
// First challenge of this level type
|
2018-02-20 04:50:34 +01:00
|
|
|
List<ChallengesData> challenges = new ArrayList<>();
|
2017-11-27 08:13:17 +01:00
|
|
|
challenges.add(challenge);
|
|
|
|
challengeList.put(level, challenges);
|
|
|
|
}
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
|
|
|
| InvocationTargetException | SecurityException | ClassNotFoundException | IntrospectionException
|
2017-11-27 08:13:17 +01:00
|
|
|
| SQLException | NoSuchMethodException e) {
|
2017-11-26 03:17:16 +01:00
|
|
|
// TODO Auto-generated catch block
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: " + challengeList.size());
|
2017-11-27 08:13:17 +01:00
|
|
|
// Sort the challenge list into level order
|
|
|
|
challengeList = challengeList.entrySet().stream()
|
|
|
|
.sorted(Map.Entry.comparingByKey())
|
|
|
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
|
|
|
|
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
|
2018-02-23 08:52:39 +01:00
|
|
|
Bukkit.getLogger().info("DEBUG: " + challengeList.size());
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save to the database
|
|
|
|
* @param async - if true, saving will be done async
|
|
|
|
*/
|
|
|
|
public void save(boolean async){
|
|
|
|
if(async){
|
|
|
|
Runnable save = () -> {
|
2018-02-20 04:50:34 +01:00
|
|
|
for (Entry<LevelsDO, List<ChallengesData>> en : challengeList.entrySet()) {
|
2017-11-26 03:17:16 +01:00
|
|
|
try {
|
2017-11-27 08:13:17 +01:00
|
|
|
lvHandler.saveObject(en.getKey());
|
|
|
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
|
|
|
| SecurityException | InstantiationException | NoSuchMethodException
|
|
|
|
| IntrospectionException | SQLException e) {
|
|
|
|
// TODO Auto-generated catch block
|
2017-11-26 03:17:16 +01:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2018-02-20 04:50:34 +01:00
|
|
|
for (ChallengesData challenge : en.getValue()) {
|
2017-11-27 08:13:17 +01:00
|
|
|
try {
|
|
|
|
chHandler.saveObject(challenge);
|
|
|
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
|
|
|
| SecurityException | InstantiationException | NoSuchMethodException
|
|
|
|
| IntrospectionException | SQLException e) {
|
|
|
|
// TODO Auto-generated catch block
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
};
|
2017-12-29 05:39:07 +01:00
|
|
|
BSkyBlock.getInstance().getServer().getScheduler().runTaskAsynchronously(BSkyBlock.getInstance(), save);
|
2017-11-26 03:17:16 +01:00
|
|
|
} else {
|
2018-02-20 04:50:34 +01:00
|
|
|
for (Entry<LevelsDO, List<ChallengesData>> en : challengeList.entrySet()) {
|
2017-11-26 03:17:16 +01:00
|
|
|
try {
|
2017-11-27 08:13:17 +01:00
|
|
|
lvHandler.saveObject(en.getKey());
|
|
|
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
|
|
|
| SecurityException | InstantiationException | NoSuchMethodException | IntrospectionException
|
|
|
|
| SQLException e) {
|
|
|
|
// TODO Auto-generated catch block
|
2017-11-26 03:17:16 +01:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2018-02-20 04:50:34 +01:00
|
|
|
for (ChallengesData challenge : en.getValue()) {
|
2017-11-27 08:13:17 +01:00
|
|
|
try {
|
|
|
|
chHandler.saveObject(challenge);
|
|
|
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
|
|
|
| SecurityException | InstantiationException | NoSuchMethodException
|
|
|
|
| IntrospectionException | SQLException e) {
|
|
|
|
// TODO Auto-generated catch block
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void shutdown(){
|
|
|
|
save(false);
|
2017-11-27 08:13:17 +01:00
|
|
|
challengeList.clear();
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a challenge from the inventory contents
|
|
|
|
* @param contents
|
|
|
|
*/
|
2017-12-29 05:39:07 +01:00
|
|
|
public void createChallenge(User user, String name) {
|
2017-11-26 03:17:16 +01:00
|
|
|
// Get the main icon
|
2017-12-29 05:39:07 +01:00
|
|
|
ItemStack icon = user.getInventory().getItemInOffHand();
|
2017-11-26 03:17:16 +01:00
|
|
|
if (icon == null || icon.getType().equals(Material.AIR)) {
|
2018-02-20 04:50:34 +01:00
|
|
|
user.sendRawMessage("Hold something in your off-hand to make it the icon. Icon will be paper be default.");
|
2017-11-26 03:17:16 +01:00
|
|
|
icon = new ItemStack(Material.PAPER);
|
|
|
|
}
|
|
|
|
icon.setAmount(1);
|
|
|
|
ItemMeta meta = icon.getItemMeta();
|
|
|
|
meta.setDisplayName(name);
|
|
|
|
List<String> lore = new ArrayList<>();
|
|
|
|
lore.add("Required items:");
|
|
|
|
|
2017-12-29 05:39:07 +01:00
|
|
|
List<ItemStack> inv = Arrays.asList(user.getInventory().getStorageContents());
|
2017-11-26 03:17:16 +01:00
|
|
|
List<ItemStack> contents = new ArrayList<>();
|
|
|
|
for (ItemStack item : inv) {
|
|
|
|
if (item != null && !item.getType().equals(Material.AIR)) {
|
|
|
|
contents.add(item);
|
|
|
|
lore.add(item.getType() + " x " + item.getAmount());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (lore.size() == 1) {
|
|
|
|
lore.add("No items");
|
|
|
|
}
|
|
|
|
meta.setDisplayName(name);
|
|
|
|
meta.setLore(lore);
|
|
|
|
icon.setItemMeta(meta);
|
2018-02-20 04:50:34 +01:00
|
|
|
ChallengesData newChallenge = new ChallengesData();
|
2017-11-26 03:17:16 +01:00
|
|
|
newChallenge.setRequiredItems(contents);
|
|
|
|
newChallenge.setUniqueId(name);
|
|
|
|
newChallenge.setIcon(icon);
|
2018-02-20 04:50:34 +01:00
|
|
|
if (chHandler.objectExists(name)) {
|
|
|
|
user.sendRawMessage(ChatColor.RED + "Challenge already exists! Use /c replace <name>");
|
2017-11-26 03:17:16 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
chHandler.saveObject(newChallenge);
|
|
|
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException
|
|
|
|
| InstantiationException | NoSuchMethodException | IntrospectionException | SQLException e) {
|
|
|
|
// TODO Auto-generated catch block
|
|
|
|
e.printStackTrace();
|
2018-02-20 04:50:34 +01:00
|
|
|
user.sendRawMessage(ChatColor.RED + "Challenge creation failed! " + e.getMessage());
|
2017-11-26 03:17:16 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-20 04:50:34 +01:00
|
|
|
user.sendRawMessage("Challenge accepted!");
|
2017-11-27 08:13:17 +01:00
|
|
|
// TODO ADD CHALLENGE
|
|
|
|
//challenges.put(newChallenge.getUniqueId(), newChallenge);
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
|
2017-11-27 08:13:17 +01:00
|
|
|
/**
|
|
|
|
* Get the list of challenges for this level
|
|
|
|
* @param level - the level required
|
|
|
|
* @return the list of challenges for this level, or the first set of challenges if level is blank, or a blank list if there are no challenges
|
|
|
|
*/
|
2018-02-20 04:50:34 +01:00
|
|
|
public List<ChallengesData> getChallenges(String level) {
|
|
|
|
return challengeList.getOrDefault(level, challengeList.isEmpty() ? new ArrayList<ChallengesData>() : challengeList.values().iterator().next());
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
2017-11-27 08:13:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if a challenge is complete or not
|
2018-02-20 04:50:34 +01:00
|
|
|
* @param uniqueId - unique ID - player's UUID
|
2017-11-27 08:13:17 +01:00
|
|
|
* @param uniqueId2 - Challenge id
|
|
|
|
* @return - true if completed
|
|
|
|
*/
|
2018-02-23 08:52:39 +01:00
|
|
|
public boolean isChallengeComplete(User user, String uniqueId2) {
|
2017-11-27 08:13:17 +01:00
|
|
|
// TODO Auto-generated method stub
|
|
|
|
return false;
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
|
2018-02-23 08:52:39 +01:00
|
|
|
public boolean isLevelComplete(User user, LevelsDO otherLevel) {
|
2017-11-27 08:13:17 +01:00
|
|
|
// TODO Auto-generated method stub
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public LevelsDO getPreviousLevel(LevelsDO otherLevel) {
|
|
|
|
LevelsDO result = null;
|
|
|
|
|
|
|
|
for (LevelsDO level : challengeList.keySet()) {
|
|
|
|
if (level.equals(otherLevel)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
result = level;
|
|
|
|
}
|
|
|
|
return result;
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-11-27 08:13:17 +01:00
|
|
|
* Get the status on every level
|
2017-12-29 05:39:07 +01:00
|
|
|
* @param user
|
2017-11-27 08:13:17 +01:00
|
|
|
* @return Level name, how many challenges still to do on which level
|
2017-11-26 03:17:16 +01:00
|
|
|
*/
|
2017-12-29 05:39:07 +01:00
|
|
|
public List<LevelStatus> getChallengeLevelStatus(User user) {
|
2017-11-27 08:13:17 +01:00
|
|
|
List<LevelStatus> result = new ArrayList<>();
|
|
|
|
LevelsDO previousLevel = null;
|
2018-02-20 04:50:34 +01:00
|
|
|
for (Entry<LevelsDO, List<ChallengesData>> en : challengeList.entrySet()) {
|
2017-11-27 08:13:17 +01:00
|
|
|
int challsToDo = 0; // TODO - calculate how many challenges still to do for this player
|
|
|
|
boolean complete = false; // TODO
|
|
|
|
result.add(new LevelStatus(en.getKey(), previousLevel, challsToDo, complete));
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
2017-11-27 08:13:17 +01:00
|
|
|
return result;
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
|
|
|
|
2017-11-27 08:13:17 +01:00
|
|
|
public class LevelStatus {
|
|
|
|
private final LevelsDO level;
|
|
|
|
private final LevelsDO previousLevel;
|
|
|
|
private final int numberOfChallengesStillToDo;
|
|
|
|
private final boolean complete;
|
|
|
|
|
|
|
|
public LevelStatus(LevelsDO level, LevelsDO previousLevel, int numberOfChallengesStillToDo, boolean complete) {
|
|
|
|
super();
|
|
|
|
this.level = level;
|
|
|
|
this.previousLevel = previousLevel;
|
|
|
|
this.numberOfChallengesStillToDo = numberOfChallengesStillToDo;
|
|
|
|
this.complete = complete;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @return the level
|
|
|
|
*/
|
|
|
|
public LevelsDO getLevel() {
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @return the previousLevel
|
|
|
|
*/
|
|
|
|
public LevelsDO getPreviousLevel() {
|
|
|
|
return previousLevel;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @return the numberOfChallengesStillToDo
|
|
|
|
*/
|
|
|
|
public int getNumberOfChallengesStillToDo() {
|
|
|
|
return numberOfChallengesStillToDo;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @return the complete
|
|
|
|
*/
|
|
|
|
public boolean isComplete() {
|
|
|
|
return complete;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|
2018-02-20 04:50:34 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates an inventory challenge
|
|
|
|
* @param user
|
|
|
|
* @param inventory
|
|
|
|
*/
|
|
|
|
public void createInvChallenge(User user, Inventory inventory) {
|
|
|
|
if (inventory.getContents().length == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ChallengesData newChallenge = new ChallengesData();
|
|
|
|
newChallenge.setChallengeType(ChallengeType.INVENTORY);
|
|
|
|
newChallenge.setFriendlyName(inventory.getTitle());
|
|
|
|
newChallenge.setDeployed(false);
|
|
|
|
List<ItemStack> requiredItems = new ArrayList<>();
|
|
|
|
inventory.forEach(item -> {
|
|
|
|
if (item != null && !item.getType().equals(Material.AIR)) {
|
|
|
|
requiredItems.add(item);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
newChallenge.setRequiredItems(requiredItems);
|
|
|
|
newChallenge.setTakeItems(true);
|
|
|
|
newChallenge.setUniqueId(inventory.getTitle());
|
|
|
|
newChallenge.setIcon(new ItemStack(Material.EMPTY_MAP));
|
2018-02-23 08:52:39 +01:00
|
|
|
newChallenge.setFreeChallenge(true);
|
|
|
|
newChallenge.setLevel("");
|
2018-02-20 04:50:34 +01:00
|
|
|
|
|
|
|
// Move all the items back to the player's inventory
|
|
|
|
inventory.forEach(item -> {
|
|
|
|
if (item != null) {
|
|
|
|
Map<Integer, ItemStack> residual = user.getInventory().addItem(item);
|
|
|
|
// Drop any residual items at the foot of the player
|
|
|
|
residual.forEach((k, v) -> {
|
|
|
|
user.getWorld().dropItem(user.getLocation(), v);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Save the challenge
|
|
|
|
try {
|
|
|
|
chHandler.saveObject(newChallenge);
|
|
|
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException
|
|
|
|
| InstantiationException | NoSuchMethodException | IntrospectionException | SQLException e) {
|
|
|
|
// TODO Auto-generated catch block
|
|
|
|
e.printStackTrace();
|
|
|
|
user.sendRawMessage(ChatColor.RED + "Challenge creation failed! " + e.getMessage());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
user.sendRawMessage("Success");
|
|
|
|
}
|
2018-02-23 08:52:39 +01:00
|
|
|
|
|
|
|
public boolean isLevelAvailable(User user, String level) {
|
|
|
|
// TODO
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long checkChallengeTimes(User user, ChallengesData challenge) {
|
|
|
|
// TODO Auto-generated method stub
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Challenges getAddon() {
|
|
|
|
return addon;
|
|
|
|
}
|
2017-11-27 08:13:17 +01:00
|
|
|
|
2017-11-26 03:17:16 +01:00
|
|
|
}
|