package world.bentobox.challenges.managers; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.annotations.Expose; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory; import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.util.ItemParser; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; import world.bentobox.challenges.database.object.ChallengeLevel; import world.bentobox.challenges.database.object.requirements.InventoryRequirements; import world.bentobox.challenges.database.object.requirements.IslandRequirements; import world.bentobox.challenges.database.object.requirements.OtherRequirements; import world.bentobox.challenges.database.object.requirements.StatisticRequirements; import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; /** * Imports challenges * @author BONNe1704 * */ public class ChallengesImportManager { /** * Import challenges from file or link. * @param challengesAddon Challenges addon. */ public ChallengesImportManager(ChallengesAddon challengesAddon) { this.addon = challengesAddon; } // --------------------------------------------------------------------- // Section: YAML Importers // --------------------------------------------------------------------- /** * This method imports generator tiers from template * * @param user - user * @param world - world to import into * @param file - file that must be imported */ public void importFile(@Nullable User user, World world, String file) { File generatorFile = new File(this.addon.getDataFolder(), file.endsWith(".yml") ? file : file + ".yml"); if (!generatorFile.exists()) { if (user != null) { Utils.sendMessage(user, world, Constants.ERRORS + "no-file", Constants.PARAMETER_FILE, file); } return; } YamlConfiguration config = new YamlConfiguration(); try { config.load(generatorFile); } catch (IOException | InvalidConfigurationException e) { if (user != null) { Utils.sendMessage(user, world, Constants.ERRORS + "no-load", Constants.PARAMETER_FILE, file, TextVariables.DESCRIPTION, e.getMessage()); } this.addon.logError("Exception when loading file. " + e.getMessage()); return; } Optional optional = this.addon.getPlugin().getIWM().getAddon(world); if (optional.isEmpty()) { if (user != null) { Utils.sendMessage(user, world, Constants.ERRORS + "not-a-gamemode-world", Constants.PARAMETER_WORLD, world.getName()); } this.addon.logWarning("Given world is not a gamemode world."); return; } this.addon.getChallengesManager().wipeDatabase(optional.get().getDescription().getName().toLowerCase()); this.createChallenges(config, user, optional.get(), world); } /** * This method creates generator tier object from config file. * * @param config YamlConfiguration that contains all generators. * @param user User who calls reading. * @param gameMode GameMode in which generator tiers must be imported */ private void createChallenges(YamlConfiguration config, @Nullable User user, GameModeAddon gameMode, World world) { final String prefix = gameMode.getDescription().getName().toLowerCase() + "_"; long challengeCount = 0; long levelCount = 0; if (config.contains("challenges")) { ConfigurationSection reader = config.getConfigurationSection("challenges"); if (reader != null) { challengeCount = reader.getKeys(false).stream(). mapToInt(challengeId -> this.createChallenge(challengeId, prefix, world, reader.getConfigurationSection(challengeId))). sum(); } } if (config.contains("levels")) { ConfigurationSection reader = config.getConfigurationSection("levels"); if (reader != null) { levelCount = reader.getKeys(false).stream(). mapToInt(levelId -> this.createLevel(levelId, prefix, world, reader.getConfigurationSection(levelId))). sum(); } } if (user != null) { Utils.sendMessage(user, world, Constants.MESSAGES + "import-count", "[levels]", String.valueOf(levelCount), "[challenges]", String.valueOf(challengeCount)); } this.addon.log("Imported " + challengeCount + " challenges and " + levelCount + " levels into database."); } /** * This method creates challenge from given config section. * @param challengeId Challenge ID. * @param prefix GameMode prefix. * @param world world where challenge is created. * @param section Configuration Section that contains information. * @return 1 if challenge is created, otherwise 0. */ private int createChallenge(String challengeId, String prefix, World world, @Nullable ConfigurationSection section) { if (section == null) { return 0; } try { Challenge challenge = new Challenge(); challenge.setUniqueId(prefix + challengeId); challenge.setFriendlyName(section.getString("name", challengeId)); challenge.setIcon(matchIcon(section.getString("icon"), new ItemStack(Material.PAPER))); // Read description if (section.isList("description")) { challenge.setDescription(section.getStringList("description")); } else if (section.isString("description")) { String description = section.getString("description"); if (description != null) { // Define as list. challenge.setDescription(Arrays.asList( description.replaceAll("\\|", "\n"). split("\n").clone())); } } challenge.setDeployed(section.getBoolean("deployed", true)); challenge.setOrder(section.getInt("order", 0)); challenge.setChallengeType(matchChallengeType(section.getString("type"), Challenge.ChallengeType.ISLAND_TYPE)); // Read environment Set environments = new HashSet<>(); challenge.setEnvironment(environments); if (section.isList("environments")) { section.getStringList("environments"). forEach(text -> environments.add(matchEnvironment(text, World.Environment.NORMAL))); } else if (section.isString("environments")) { environments.add(matchEnvironment(section.getString("environments"), World.Environment.NORMAL)); } challenge.setRemoveWhenCompleted(section.getBoolean("remove-completed", false)); // Read Requirements this.populateRequirements(challenge, section.getConfigurationSection("requirements")); // Read Rewards this.populateRewards(challenge, section.getConfigurationSection("rewards")); // Check Repeating status challenge.setRepeatable(section.getBoolean("repeatable", false)); challenge.setMaxTimes(section.getInt("repeat-times", -1)); if (challenge.isRepeatable()) { // Read Repeat Rewards this.populateRepeatRewards(challenge, section.getConfigurationSection("repeat-rewards")); } this.addon.getChallengesManager().saveChallenge(challenge); this.addon.getChallengesManager().loadChallenge(challenge, world, true, null, true); } catch (Exception e) { return 0; } return 1; } /** * Populates requirements for the given challenge. * * @param challenge the challenge * @param section the section */ private void populateRequirements(Challenge challenge, ConfigurationSection section) { switch (challenge.getChallengeType()) { case INVENTORY_TYPE -> { InventoryRequirements requirements = new InventoryRequirements(); challenge.setRequirements(requirements); requirements.setTakeItems(section.getBoolean("take-items", false)); List requiredItems = new ArrayList<>(); requirements.setRequiredItems(requiredItems); if (section.isList("items")) { section.getStringList("items"). forEach(text -> { ItemStack itemStack = ItemParser.parse(text); if (itemStack != null) { requiredItems.add(itemStack); } }); } } case ISLAND_TYPE -> { IslandRequirements requirements = new IslandRequirements(); challenge.setRequirements(requirements); requirements.setRemoveBlocks(section.getBoolean("remove-blocks", false)); requirements.setRequiredBlocks(this.createMaterialMap(section.getConfigurationSection("blocks"))); requirements.setRemoveEntities(section.getBoolean("remove-entities", false)); requirements.setRequiredEntities(this.createEntityMap(section.getConfigurationSection("entities"))); requirements.setSearchRadius(section.getInt("search-distance", 10)); } case OTHER_TYPE -> { OtherRequirements requirements = new OtherRequirements(); challenge.setRequirements(requirements); requirements.setTakeMoney(section.getBoolean("take-money", false)); requirements.setRequiredMoney(section.getDouble("money", 0)); requirements.setTakeExperience(section.getBoolean("take-experience", false)); requirements.setRequiredExperience(section.getInt("experience", 0)); requirements.setRequiredIslandLevel(section.getInt("level", 0)); } case STATISTIC_TYPE -> { StatisticRequirements requirements = new StatisticRequirements(); challenge.setRequirements(requirements); requirements.setAmount(section.getInt("amount", 0)); requirements.setReduceStatistic(section.getBoolean("reduce", false)); requirements.setStatistic(matchStatistic(section.getString("statistic"))); requirements.setEntity(matchEntity(section.getString("entity"))); requirements.setMaterial(matchMaterial(section.getString("material"))); } } // Read permissions if (challenge.getRequirements() != null) { Set permissions = new HashSet<>(); challenge.getRequirements().setRequiredPermissions(permissions); if (section.isList("permissions")) { permissions.addAll(section.getStringList("permissions")); } else if (section.isString("permissions")) { String description = section.getString("permissions"); if (description != null) { // Define as list. permissions.addAll(Arrays.asList( description.replaceAll("\\|", "\n"). split("\n").clone())); } } } } /** * This method populates material map from given section field. * @param section Section that contains material. * @return Map that links material and number. */ private Map createMaterialMap(ConfigurationSection section) { Map materialMaps = new HashMap<>(); if (section != null) { for (String materialKey : section.getKeys(false)) { Material material = matchMaterial(materialKey); if (material != null) { materialMaps.put(material, section.getInt(materialKey, 0)); } } } return materialMaps; } /** * This method populates entity map from given section field. * @param section Section that contains material. * @return Map that links entity and number. */ private Map createEntityMap(ConfigurationSection section) { Map entityMap = new HashMap<>(); if (section != null) { for (String EntityType : section.getKeys(false)) { EntityType entity = matchEntity(EntityType); if (entity != null) { entityMap.put(entity, section.getInt(EntityType, 0)); } } } return entityMap; } /** * This method populates rewards for a challenge. * @param challenge Challenge * @param section Section that contains rewards */ private void populateRewards(Challenge challenge, @Nullable ConfigurationSection section) { List rewardItems = new ArrayList<>(); challenge.setRewardItems(rewardItems); if (section == null) { return; } challenge.setRewardText(section.getString("text", "")); if (section.isList("items")) { section.getStringList("items"). forEach(text -> { ItemStack itemStack = ItemParser.parse(text); if (itemStack != null) { rewardItems.add(itemStack); } }); } challenge.setRewardExperience(section.getInt("experience", 0)); challenge.setRewardMoney(section.getDouble("money", 0)); if (section.isList("commands")) { challenge.setRewardCommands(section.getStringList("commands")); } else if (section.isString("commands")) { String description = section.getString("commands"); if (description != null) { // Define as list. challenge.setRewardCommands(Arrays.asList( description.replaceAll("\\|", "\n"). split("\n").clone())); } } } /** * This method populates repeat rewards for a challenge. * @param challenge Challenge * @param section Section that contains rewards */ private void populateRepeatRewards(Challenge challenge, @Nullable ConfigurationSection section) { List rewardItems = new ArrayList<>(); challenge.setRepeatItemReward(rewardItems); if (section == null) { return; } challenge.setRepeatRewardText(section.getString("text", "")); if (section.isList("items")) { section.getStringList("items"). forEach(text -> { ItemStack itemStack = ItemParser.parse(text); if (itemStack != null) { rewardItems.add(itemStack); } }); } challenge.setRepeatExperienceReward(section.getInt("experience", 0)); challenge.setRepeatMoneyReward(section.getDouble("money", 0)); if (section.isList("commands")) { challenge.setRepeatRewardCommands(section.getStringList("commands")); } else if (section.isString("commands")) { String description = section.getString("commands"); if (description != null) { // Define as list. challenge.setRepeatRewardCommands(Arrays.asList( description.replaceAll("\\|", "\n"). split("\n").clone())); } } } /** * This method populates rewards for a level. * @param level level * @param section Section that contains rewards */ private void populateRewards(ChallengeLevel level, @Nullable ConfigurationSection section) { List rewardItems = new ArrayList<>(); level.setRewardItems(rewardItems); if (section == null) { return; } level.setRewardText(section.getString("text", "")); if (section.isList("items")) { section.getStringList("items"). forEach(text -> { ItemStack itemStack = ItemParser.parse(text); if (itemStack != null) { rewardItems.add(itemStack); } }); } level.setRewardExperience(section.getInt("experience", 0)); level.setRewardMoney(section.getDouble("money", 0)); if (section.isList("commands")) { level.setRewardCommands(section.getStringList("commands")); } else if (section.isString("commands")) { String description = section.getString("commands"); if (description != null) { // Define as list. level.setRewardCommands(Arrays.asList( description.replaceAll("\\|", "\n"). split("\n").clone())); } } } /** * This method creates Level * @param levelId Level Id * @param prefix Gamemode prefix * @param world World where level operates. * @param section Section that contains level info. * @return 1 if level created, 0 otherwise. */ private int createLevel(String levelId, String prefix, World world, @Nullable ConfigurationSection section) { if (section == null) { return 0; } try { ChallengeLevel level = new ChallengeLevel(); level.setUniqueId(prefix + levelId); level.setFriendlyName(section.getString("name", levelId)); level.setIcon(matchIcon(section.getString("icon"), new ItemStack(Material.PAPER))); level.setLockedIcon(matchIcon(section.getString("icon"))); level.setWorld(world.getName()); level.setOrder(section.getInt("order", 0)); level.setWaiverAmount(section.getInt("waiver", 0)); level.setUnlockMessage(section.getString("description", "")); this.populateRewards(level, section.getConfigurationSection("rewards")); Set challenges = new HashSet<>(); level.setChallenges(challenges); if (section.isList("challenges")) { section.getStringList("challenges").forEach(text -> { Challenge challenge = this.addon.getChallengesManager().getChallenge(prefix + text); if (challenge != null) { challenges.add(challenge.getUniqueId()); this.addon.getChallengesManager().addChallengeToLevel(challenge, level); } }); } this.addon.getChallengesManager().saveLevel(level); this.addon.getChallengesManager().loadLevel(level, world,true, null, true); } catch (Exception ignored) { return 0; } return 1; } // --------------------------------------------------------------------- // Section: JSON Importers // --------------------------------------------------------------------- /** * Import database file from local storage. * * @param user the user * @param world the world * @param fileName the file name */ public void importDatabaseFile(User user, World world, String fileName) { World correctWorld = Util.getWorld(world); if (correctWorld == null) { this.addon.logError("Given world is not part of BentoBox"); return; } ChallengesManager manager = this.addon.getChallengesManager(); // If exist any generator that is bound to current world, then do not load generators. if (manager.hasAnyChallengeData(world.getName())) { this.addon.getPlugin().getIWM().getAddon(world).ifPresent(gameModeAddon -> manager.wipeDatabase(gameModeAddon.getDescription().getName().toLowerCase())); } try { // This prefix will be used to all generators. That is a unique way how to separate generators for // each game mode. String uniqueIDPrefix = Utils.getGameMode(world).toLowerCase() + "_"; DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadObject(fileName); if (downloadedChallenges == null) { return; } // All new challenges should get correct ID. So we need to map it to loaded challenges. downloadedChallenges.getChallengeList().forEach(challenge -> { // Set correct challenge ID challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); // Set up correct level ID if it is necessary if (!challenge.getLevel().isEmpty()) { challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); } // Load challenge in memory manager.loadChallenge(challenge, world, false, user, user == null); }); downloadedChallenges.getLevelList().forEach(challengeLevel -> { // Set correct level ID challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); // Set correct world name challengeLevel.setWorld(correctWorld.getName()); // Reset names for all challenges. challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). map(challenge -> uniqueIDPrefix + challenge). collect(Collectors.toSet())); // Load level in memory manager.loadLevel(challengeLevel, world, false, user, user == null); }); } catch (Exception e) { this.addon.getPlugin().logStacktrace(e); return; } manager.saveChallenges(); manager.saveLevels(); } /** * This method loads downloaded challenges into memory. * @param user User who calls downloaded challenge loading * @param world Target world. * @param downloadString String that need to be loaded via DefaultDataHolder. */ public void loadDownloadedChallenges(User user, World world, String downloadString) { World correctWorld = Util.getWorld(world); if (correctWorld == null) { this.addon.logError("Given world is not part of BentoBox"); return; } ChallengesManager manager = this.addon.getChallengesManager(); // If exist any challenge or level that is bound to current world, then do not load default challenges. if (manager.hasAnyChallengeData(world.getName())) { if (user.isPlayer()) { Utils.sendMessage(user, world, Constants.ERRORS + "exist-challenges-or-levels"); } else { this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); } return; } try { // This prefix will be used to all challenges. That is a unique way how to separate challenged for // each game mode. String uniqueIDPrefix = Utils.getGameMode(world).toLowerCase() + "_"; DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadWebObject(downloadString); // All new challenges should get correct ID. So we need to map it to loaded challenges. downloadedChallenges.getChallengeList().forEach(challenge -> { // Set correct challenge ID challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); // Set up correct level ID if it is necessary if (!challenge.getLevel().isEmpty()) { challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); } // Load challenge in memory manager.loadChallenge(challenge, world, false, user, user == null); }); downloadedChallenges.getLevelList().forEach(challengeLevel -> { // Set correct level ID challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); // Set correct world name challengeLevel.setWorld(correctWorld.getName()); // Reset names for all challenges. challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). map(challenge -> uniqueIDPrefix + challenge). collect(Collectors.toSet())); // Load level in memory manager.loadLevel(challengeLevel, world, false, user, user == null); }); } catch (Exception e) { this.addon.getPlugin().logStacktrace(e); return; } this.addon.getChallengesManager().saveChallenges(); this.addon.getChallengesManager().saveLevels(); } // --------------------------------------------------------------------- // Section: Default generation // --------------------------------------------------------------------- public void generateDatabaseFile(User user, World world, String fileName) { File defaultFile = new File(this.addon.getDataFolder(), fileName.endsWith(".json") ? fileName : fileName + ".json"); if (defaultFile.exists()) { if (user.isPlayer()) { Utils.sendMessage(user, world, Constants.ERRORS + "file-exist", Constants.PARAMETER_FILE, fileName); } else { this.addon.logWarning(Constants.ERRORS + "file-exist"); } return; } try { if (defaultFile.createNewFile()) { String replacementString = Utils.getGameMode(world).toLowerCase() + "_"; ChallengesManager manager = this.addon.getChallengesManager(); List challengeList = manager.getAllChallenges(world). stream(). map(challenge -> { // Use clone to avoid any changes in existing challenges. Challenge clone = challenge.clone(); // Remove world name from challenge id. clone.setUniqueId(challenge.getUniqueId().replaceFirst(replacementString, "")); // Remove world name from level id. clone.setLevel(challenge.getLevel().replaceFirst(replacementString, "")); return clone; }). collect(Collectors.toList()); List levelList = manager.getLevels(world). stream(). map(challengeLevel -> { // Use clone to avoid any changes in existing levels. ChallengeLevel clone = challengeLevel.copy(); // Remove world name from level ID. clone.setUniqueId(challengeLevel.getUniqueId().replaceFirst(replacementString, "")); // Remove world name. clone.setWorld(""); // Challenges must be reassign, as they also contains world name. clone.setChallenges(challengeLevel.getChallenges().stream(). map(challenge -> challenge.replaceFirst(replacementString, "")). collect(Collectors.toSet())); return clone; }). collect(Collectors.toList()); DefaultDataHolder defaultChallenges = new DefaultDataHolder(); defaultChallenges.setChallengeList(challengeList); defaultChallenges.setLevelList(levelList); defaultChallenges.setVersion(this.addon.getDescription().getVersion()); try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8))) { writer.write(Objects.requireNonNull( new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); } } } catch (IOException e) { if (user.isPlayer()) { Utils.sendMessage(user, world, Constants.ERRORS + "no-load", Constants.PARAMETER_FILE, fileName, TextVariables.DESCRIPTION, e.getMessage()); } this.addon.logError("Could not save json file: " + e.getMessage()); } finally { if (user.isPlayer()) { Utils.sendMessage(user, world, Constants.CONVERSATIONS + "database-export-completed", Constants.PARAMETER_WORLD, world.getName(), Constants.PARAMETER_FILE, fileName); } else { this.addon.logWarning("Database Export Completed"); } } } // --------------------------------------------------------------------- // Section: Static Methods // --------------------------------------------------------------------- /** * Match item stack. * * @param text the text * @return the item stack */ @Nullable private static ItemStack matchIcon(@Nullable String text) { if (text == null || text.isBlank()) { return new ItemStack(Material.PAPER); } else { return ItemParser.parse(text, new ItemStack(Material.PAPER)); } } /** * Match item stack. * * @param text the text * @param defaultItem the default item * @return the item stack */ @NonNull private static ItemStack matchIcon(@Nullable String text, ItemStack defaultItem) { ItemStack item = matchIcon(text); return item == null ? defaultItem : item; } /** * Match material. * * @param text the text * @return the material */ @Nullable private static Material matchMaterial(@Nullable String text) { if (text == null || text.isBlank()) { return null; } else { return Material.getMaterial(text.toUpperCase()); } } /** * Match material. * * @param text the text * @param defaultItem the default item * @return the material */ @NonNull private static Material matchMaterial(@Nullable String text, Material defaultItem) { Material item = matchMaterial(text); return item == null ? defaultItem : item; } /** * Match entity type. * * @param text the text * @return the entity type */ @Nullable private static EntityType matchEntity(@Nullable String text) { if (text == null || text.isBlank()) { return null; } else { try { return EntityType.valueOf(text.toUpperCase()); } catch (Exception e) { return null; } } } /** * Match entity type. * * @param text the text * @param defaultItem the default item * @return the entity type */ @NonNull private static EntityType matchEntity(@Nullable String text, EntityType defaultItem) { EntityType item = matchEntity(text); return item == null ? defaultItem : item; } /** * Match statistic value. * * @param text the text * @return the statistic */ @Nullable private static Statistic matchStatistic(@Nullable String text) { if (text == null || text.isBlank()) { return null; } else { try { return Statistic.valueOf(text.toUpperCase()); } catch (Exception e) { return null; } } } /** * Match challenge type * * @param text the text * @param defaultType default type * @return the challenge type */ private static Challenge.ChallengeType matchChallengeType(@Nullable String text, Challenge.ChallengeType defaultType) { if (text == null || text.isBlank()) { return defaultType; } else { try { return Challenge.ChallengeType.valueOf(text.toUpperCase()); } catch (Exception e) { return defaultType; } } } /** * Match world environment. * * @param text the text * @param defaultType the default type * @return the world environment */ private static World.Environment matchEnvironment(@Nullable String text, World.Environment defaultType) { if (text == null || text.isBlank()) { return defaultType; } else { try { return World.Environment.valueOf(text.toUpperCase()); } catch (Exception e) { return defaultType; } } } // --------------------------------------------------------------------- // Section: Private classes for default challenges // --------------------------------------------------------------------- /** * This Class allows to load default challenges and their levels as objects much easier. */ private static final class DefaultJSONHandler { /** * This constructor inits JSON builder that will be used to parse challenges. * @param addon Challenges Adddon */ DefaultJSONHandler(ChallengesAddon addon) { GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); // Register adapters builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(addon.getPlugin())); // Keep null in the database builder.serializeNulls(); // Allow characters like < or > without escaping them builder.disableHtmlEscaping(); this.addon = addon; this.gson = builder.setPrettyPrinting().create(); } /** * This method returns json object that is parsed to string. Json object is made from given instance. * @param instance Instance that must be parsed to json string. * @return String that contains JSON information from instance object. */ String toJsonString(DefaultDataHolder instance) { // Null check if (instance == null) { this.addon.logError("JSON database request to store a null. "); return null; } return this.gson.toJson(instance); } /** * This method creates and adds to list all objects from default.json file. * @return List of all objects from default.json that is with T instance. */ DefaultDataHolder loadObject(String fileName) { if (!fileName.endsWith(".json")) { fileName = fileName + ".json"; } File defaultFile = new File(this.addon.getDataFolder(), fileName); try (InputStreamReader reader = new InputStreamReader(new FileInputStream(defaultFile), StandardCharsets.UTF_8)) { DefaultDataHolder object = this.gson.fromJson(reader, DefaultDataHolder.class); reader.close(); // NOSONAR Required to keep OS file handlers low and not rely on GC return object; } catch (FileNotFoundException e) { this.addon.logError("Could not load file '" + defaultFile.getName() + "': File not found."); } catch (Exception e) { this.addon.logError("Could not load objects " + defaultFile.getName() + " " + e.getMessage()); } return null; } /** * This method creates and adds to list all objects from default.json file. * @return List of all objects from default.json that is with T instance. */ DefaultDataHolder loadWebObject(String downloadedObject) { return this.gson.fromJson(downloadedObject, DefaultDataHolder.class); } // --------------------------------------------------------------------- // Section: Variables // --------------------------------------------------------------------- /** * Holds JSON builder object. */ private final Gson gson; /** * Holds ChallengesAddon object. */ private final ChallengesAddon addon; } /** * This is simple object that will allow to store all current challenges and levels * in single file. */ private static final class DefaultDataHolder implements DataObject { /** * Default constructor. Creates object with empty lists. */ DefaultDataHolder() { this.challengeList = Collections.emptyList(); this.challengeLevelList = Collections.emptyList(); this.version = ""; } /** * This method returns stored challenge list. * @return list that contains default challenges. */ List getChallengeList() { return challengeList; } /** * This method sets given list as default challenge list. * @param challengeList new default challenge list. */ void setChallengeList(List challengeList) { this.challengeList = challengeList; } /** * This method returns list of default challenge levels. * @return List that contains default challenge levels. */ List getLevelList() { return challengeLevelList; } /** * This method sets given list as default challenge level list. * @param levelList new default challenge level list. */ void setLevelList(List levelList) { this.challengeLevelList = levelList; } /** * This method returns the version value. * @return the value of version. */ public String getVersion() { return version; } /** * This method sets the version value. * @param version the version new value. * */ public void setVersion(String version) { this.version = version; } /** * @return default.json */ @Override public String getUniqueId() { return "default.json"; } /** * @param uniqueId - unique ID the uniqueId to set */ @Override public void setUniqueId(String uniqueId) { // method not used. } // --------------------------------------------------------------------- // Section: Variables // --------------------------------------------------------------------- /** * Holds a list with default challenges. */ @Expose private List challengeList; /** * Holds a list with default levels. */ @Expose private List challengeLevelList; /** * Holds a variable that stores in which addon version file was made. */ @Expose private String version; } // --------------------------------------------------------------------- // Section: Variables // --------------------------------------------------------------------- private final ChallengesAddon addon; }