Add validation methods to challenge and challengeLevel.

Do not load into local cache invalid data. Add error warnings about it.
This commit is contained in:
BONNe 2020-07-26 12:10:35 +03:00
parent 7060799bcc
commit 504c0b410e
8 changed files with 141 additions and 30 deletions

View File

@ -279,6 +279,17 @@ public class ChallengesManager
return false; return false;
} }
if (!challenge.isValid())
{
if (!silent)
{
user.sendMessage("challenges.errors.invalid-challenge", "[challenge]", challenge.getUniqueId());
}
this.addon.logWarning("Data for challenge `" + challenge.getUniqueId() + "` is not valid. It could be NULL element in item-stack!");
return false;
}
if (this.challengeCacheData.containsKey(challenge.getUniqueId())) if (this.challengeCacheData.containsKey(challenge.getUniqueId()))
{ {
if (!overwrite) if (!overwrite)
@ -352,6 +363,17 @@ public class ChallengesManager
return false; return false;
} }
if (!level.isValid())
{
if (!silent)
{
user.sendMessage("challenges.errors.invalid-level", "[level]", level.getUniqueId());
}
this.addon.logWarning("Data for level `" + level.getUniqueId() + "` is not valid. It could be NULL element in item-stack!");
return false;
}
if (!this.isValidLevel(level)) if (!this.isValidLevel(level))
{ {
if (user != null) if (user != null)

View File

@ -1,12 +1,7 @@
package world.bentobox.challenges.database.object; package world.bentobox.challenges.database.object;
import java.util.ArrayList; import java.util.*;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bukkit.Material; import org.bukkit.Material;
@ -18,6 +13,7 @@ import org.eclipse.jdt.annotation.NonNull;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.JsonAdapter;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table; import world.bentobox.bentobox.database.objects.Table;
import world.bentobox.challenges.database.object.adapters.EntityCompatibilityAdapter; import world.bentobox.challenges.database.object.adapters.EntityCompatibilityAdapter;
@ -1104,6 +1100,32 @@ public class Challenge implements DataObject
} }
/**
* This method checks if variable values are valid for current level.
* @return {@code true} if all object values are valid, {@code false} otherwise.
*/
public boolean isValid()
{
return this.uniqueId != null &&
!this.uniqueId.isEmpty() &&
this.friendlyName != null &&
this.description != null &&
this.icon != null &&
this.challengeType != null &&
this.environment != null &&
this.level != null &&
this.requirements.isValid() &&
this.rewardText != null &&
this.rewardItems.stream().noneMatch(Objects::isNull) &&
this.rewardCommands != null &&
this.repeatRewardText != null &&
this.repeatItemReward.stream().noneMatch(Objects::isNull) &&
this.repeatRewardCommands != null;
}
/** /**
* Clone method that returns clone of current challenge. * Clone method that returns clone of current challenge.
* @return Challenge that is cloned from current object. * @return Challenge that is cloned from current object.
@ -1114,13 +1136,8 @@ public class Challenge implements DataObject
Challenge clone; Challenge clone;
try try
{
clone = (Challenge) super.clone();
}
catch (CloneNotSupportedException e)
{ {
clone = new Challenge(); clone = new Challenge();
clone.setUniqueId(this.uniqueId); clone.setUniqueId(this.uniqueId);
clone.setFriendlyName(this.friendlyName); clone.setFriendlyName(this.friendlyName);
clone.setDeployed(this.deployed); clone.setDeployed(this.deployed);
@ -1133,7 +1150,9 @@ public class Challenge implements DataObject
clone.setRemoveWhenCompleted(this.removeWhenCompleted); clone.setRemoveWhenCompleted(this.removeWhenCompleted);
clone.setRequirements(this.requirements.clone()); clone.setRequirements(this.requirements.clone());
clone.setRewardText(this.rewardText); clone.setRewardText(this.rewardText);
clone.setRewardItems(this.rewardItems.stream().map(ItemStack::clone). clone.setRewardItems(
this.rewardItems.stream().
map(ItemStack::clone).
collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size())))); collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size()))));
clone.setRewardExperience(this.rewardExperience); clone.setRewardExperience(this.rewardExperience);
clone.setRewardMoney(this.rewardMoney); clone.setRewardMoney(this.rewardMoney);
@ -1142,11 +1161,20 @@ public class Challenge implements DataObject
clone.setRepeatRewardText(this.repeatRewardText); clone.setRepeatRewardText(this.repeatRewardText);
clone.setMaxTimes(this.maxTimes); clone.setMaxTimes(this.maxTimes);
clone.setRepeatExperienceReward(this.repeatExperienceReward); clone.setRepeatExperienceReward(this.repeatExperienceReward);
clone.setRepeatItemReward(this.repeatItemReward.stream().map(ItemStack::clone). clone.setRepeatItemReward(
this.repeatItemReward.stream().
map(ItemStack::clone).
collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size())))); collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size()))));
clone.setRepeatMoneyReward(this.repeatMoneyReward); clone.setRepeatMoneyReward(this.repeatMoneyReward);
clone.setRepeatRewardCommands(new ArrayList<>(this.repeatRewardCommands)); clone.setRepeatRewardCommands(new ArrayList<>(this.repeatRewardCommands));
} }
catch (Exception e)
{
BentoBox.getInstance().logError("Failed to clone Challenge " + this.uniqueId);
BentoBox.getInstance().logStacktrace(e);
clone = this;
this.deployed = false;
}
return clone; return clone;
} }

View File

@ -1,10 +1,7 @@
package world.bentobox.challenges.database.object; package world.bentobox.challenges.database.object;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bukkit.Material; import org.bukkit.Material;
@ -12,11 +9,13 @@ import org.bukkit.inventory.ItemStack;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.api.configuration.ConfigComment;
import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table; import world.bentobox.bentobox.database.objects.Table;
import world.bentobox.challenges.ChallengesManager; import world.bentobox.challenges.ChallengesManager;
/** /**
* Represent a challenge level * Represent a challenge level
* @author tastybento * @author tastybento
@ -520,6 +519,25 @@ public class ChallengeLevel implements DataObject, Comparable<ChallengeLevel>
} }
/**
* This method checks if variable values are valid for current level.
* @return {@code true} if all object values are valid, {@code false} otherwise.
*/
public boolean isValid()
{
return this.uniqueId != null &&
!this.uniqueId.isEmpty() &&
this.friendlyName != null &&
this.challenges != null &&
this.icon != null &&
this.world != null &&
this.unlockMessage != null &&
this.rewardText != null &&
this.rewardItems.stream().noneMatch(Objects::isNull) &&
this.rewardCommands != null;
}
/** /**
* Clone method that returns clone of current challengeLevel. * Clone method that returns clone of current challengeLevel.
* @return ChallengeLevel that is cloned from current object. * @return ChallengeLevel that is cloned from current object.
@ -527,15 +545,10 @@ public class ChallengeLevel implements DataObject, Comparable<ChallengeLevel>
@Override @Override
public ChallengeLevel clone() public ChallengeLevel clone()
{ {
ChallengeLevel clone; ChallengeLevel clone = new ChallengeLevel();
try try
{ {
clone = (ChallengeLevel) super.clone();
}
catch (CloneNotSupportedException e)
{
clone = new ChallengeLevel();
clone.setUniqueId(this.uniqueId); clone.setUniqueId(this.uniqueId);
clone.setFriendlyName(this.friendlyName); clone.setFriendlyName(this.friendlyName);
clone.setIcon(this.icon.clone()); clone.setIcon(this.icon.clone());
@ -545,12 +558,21 @@ public class ChallengeLevel implements DataObject, Comparable<ChallengeLevel>
clone.setWaiverAmount(this.waiverAmount); clone.setWaiverAmount(this.waiverAmount);
clone.setUnlockMessage(this.unlockMessage); clone.setUnlockMessage(this.unlockMessage);
clone.setRewardText(this.rewardText); clone.setRewardText(this.rewardText);
clone.setRewardItems(this.rewardItems.stream().map(ItemStack::clone).collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size())))); clone.setRewardItems(
this.rewardItems.stream().
map(ItemStack::clone).
collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size()))));
clone.setRewardExperience(this.rewardExperience); clone.setRewardExperience(this.rewardExperience);
clone.setRewardMoney(this.rewardMoney); clone.setRewardMoney(this.rewardMoney);
clone.setRewardCommands(new ArrayList<>(this.rewardCommands)); clone.setRewardCommands(new ArrayList<>(this.rewardCommands));
clone.setChallenges(new HashSet<>(this.challenges)); clone.setChallenges(new HashSet<>(this.challenges));
} }
catch (Exception e)
{
BentoBox.getInstance().logError("Failed to clone ChallengeLevel " + this.uniqueId);
BentoBox.getInstance().logStacktrace(e);
clone = this;
}
return clone; return clone;
} }

View File

@ -10,6 +10,7 @@ package world.bentobox.challenges.database.object.requirements;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -85,6 +86,19 @@ public class InventoryRequirements extends Requirements
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/**
* Method isValid returns if given requirement data is valid or not.
*
* @return {@code true} if data is valid, {@code false} otherwise.
*/
@Override
public boolean isValid()
{
return super.isValid() &&
this.requiredItems != null && this.requiredItems.stream().noneMatch(Objects::isNull);
}
/** /**
* Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary
* to use it. * to use it.

View File

@ -7,10 +7,7 @@
package world.bentobox.challenges.database.object.requirements; package world.bentobox.challenges.database.object.requirements;
import java.util.EnumMap; import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
@ -153,6 +150,20 @@ public class IslandRequirements extends Requirements
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/**
* Method isValid returns if given requirement data is valid or not.
*
* @return {@code true} if data is valid, {@code false} otherwise.
*/
@Override
public boolean isValid()
{
return super.isValid() &&
this.requiredBlocks != null && this.requiredBlocks.keySet().stream().noneMatch(Objects::isNull) &&
this.requiredEntities != null && this.requiredEntities.keySet().stream().noneMatch(Objects::isNull);
}
/** /**
* Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary
* to use it. * to use it.

View File

@ -59,6 +59,16 @@ public abstract class Requirements
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/**
* Method isValid returns if given requirement data is valid or not.
* @return {@code true} if data is valid, {@code false} otherwise.
*/
public boolean isValid()
{
return this.requiredPermissions != null;
}
/** /**
* Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary
* to use it. * to use it.

View File

@ -1006,11 +1006,13 @@ public abstract class CommonGUI
} }
else if (meta instanceof TropicalFishBucketMeta) else if (meta instanceof TropicalFishBucketMeta)
{ {
result.add(this.user.getTranslation("challenges.gui.item-description.fish-meta", if (((TropicalFishBucketMeta) meta).hasVariant())
{
result.add(this.user.getTranslation("challenges.gui.item-description.fish-meta",
"[pattern]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPattern().name()), "[pattern]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPattern().name()),
"[pattern-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPatternColor().name()), "[pattern-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPatternColor().name()),
"[body-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getBodyColor().name()))); "[body-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getBodyColor().name())));
// parse ne }
} }
if (meta.hasEnchants()) if (meta.hasEnchants())

View File

@ -595,6 +595,8 @@ challenges:
missing-level: '&c Challenge Level [level] is not defined in the database. It may cause errors!' missing-level: '&c Challenge Level [level] is not defined in the database. It may cause errors!'
missing-arguments: '&c Command is missing arguments.' missing-arguments: '&c Command is missing arguments.'
no-multiple-permission: "&c You do not have permission to complete this challenge multiple times at once." no-multiple-permission: "&c You do not have permission to complete this challenge multiple times at once."
invalid-level: "&c Level [level] contains invalid data. It will not be loaded from database!"
invalid-challenge: "&c Challenge [challenge] contains invalid data. It will not be loaded from database!"
protection: protection:
flags: flags:
CHALLENGES_ISLAND_PROTECTION: CHALLENGES_ISLAND_PROTECTION: