Rework how challenges are stored.

This is old change that was declined (#105), but unfortunately it is necessary.
If every ID is based on world names, than addon cannot process "/" in it. It mean that worlds cannot be put into different folders.
This change will fix it, but it is not completed.

In progress.
There does not exist converter, so old data is not usable with this version.
This commit is contained in:
BONNe1704 2019-08-01 13:19:15 +03:00
parent 80ed7eab3e
commit e9f9b1b8d5
10 changed files with 155 additions and 129 deletions

View File

@ -13,6 +13,7 @@ import world.bentobox.bentobox.api.logs.LogEntry;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.challenges.database.object.Challenge;
import world.bentobox.challenges.database.object.ChallengeLevel;
@ -74,6 +75,11 @@ public class ChallengesManager
*/
private Settings settings;
/**
* Island world manager allows to detect which world refferes to which gamemode addon.
*/
private IslandWorldManager islandWorldManager;
// ---------------------------------------------------------------------
// Section: Constants
@ -98,6 +104,8 @@ public class ChallengesManager
public ChallengesManager(ChallengesAddon addon)
{
this.addon = addon;
this.islandWorldManager = addon.getPlugin().getIWM();
this.settings = addon.getChallengesSettings();
// Set up the configs
@ -366,7 +374,7 @@ public class ChallengesManager
*/
private boolean isValidLevel(@NonNull ChallengeLevel level)
{
if (!this.addon.getPlugin().getIWM().inWorld(Bukkit.getWorld(level.getWorld())))
if (!this.islandWorldManager.inWorld(Bukkit.getWorld(level.getWorld())))
{
return false;
}
@ -717,15 +725,15 @@ public class ChallengesManager
/**
* Resets all the challenges for user in world
* Resets all the challenges for user in given GameMode.
*
* @param storageDataID - island owner's UUID
* @param worldName - world
* @param gameMode - GameMode name.
*/
private void resetAllChallenges(@NonNull String storageDataID, @NonNull String worldName)
private void resetAllChallenges(@NonNull String storageDataID, @NonNull String gameMode)
{
this.addPlayerData(storageDataID);
this.playerCacheData.get(storageDataID).reset(worldName);
this.playerCacheData.get(storageDataID).reset(gameMode);
// Save
this.savePlayerData(storageDataID);
}
@ -735,15 +743,15 @@ public class ChallengesManager
* Get the status on every level for required world and playerData
*
* @param storageDataID - playerData ID
* @param worldName - World Name where levels should be searched.
* @param gameMode - World Name where levels should be searched.
* @return Level status - how many challenges still to do on which level
*/
private List<LevelStatus> getAllChallengeLevelStatus(String storageDataID, String worldName)
private List<LevelStatus> getAllChallengeLevelStatus(String storageDataID, String gameMode)
{
this.addPlayerData(storageDataID);
ChallengesPlayerData playerData = this.playerCacheData.get(storageDataID);
List<ChallengeLevel> challengeLevelList = this.getLevels(worldName);
List<ChallengeLevel> challengeLevelList = this.getLevels(gameMode);
List<LevelStatus> result = new ArrayList<>();
@ -781,22 +789,40 @@ public class ChallengesManager
/**
* This method returns LevelStatus object for given challenge level.
* @param storageDataID User which level status must be acquired.
* @param world World where level is living.
* @param level Level which status must be calculated.
* @return LevelStatus of given level.
*/
private LevelStatus getChallengeLevelStatus(@NonNull String storageDataID, @NonNull ChallengeLevel level)
private LevelStatus getChallengeLevelStatus(@NonNull String storageDataID, World world, @NonNull ChallengeLevel level)
{
List<LevelStatus> statusList = this.getAllChallengeLevelStatus(storageDataID, level.getWorld());
this.addPlayerData(storageDataID);
ChallengesPlayerData playerData = this.playerCacheData.get(storageDataID);
for (LevelStatus status : statusList)
List<ChallengeLevel> challengeLevelList = this.getLevels(world);
int levelIndex = challengeLevelList.indexOf(level);
if (levelIndex == -1)
{
if (status.getLevel().equals(level))
{
return status;
}
return null;
}
else
{
ChallengeLevel previousLevel = levelIndex < 1 ? null : challengeLevelList.get(levelIndex - 1);
return null;
int challengesToDo = previousLevel == null ? 0 :
(previousLevel.getChallenges().size() - level.getWaiverAmount());
// As level already contains unique ids of challenges, just iterate through them.
int doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count();
return new LevelStatus(
level,
previousLevel,
challengesToDo,
level.getChallenges().size() == doneChallengeCount,
challengesToDo <= 0);
}
}
@ -807,13 +833,18 @@ public class ChallengesManager
* @param level - level
* @return true if level is unlocked
*/
private boolean isLevelUnlocked(@NonNull String storageDataID, ChallengeLevel level)
private boolean isLevelUnlocked(@NonNull String storageDataID,
World world,
ChallengeLevel level)
{
this.addPlayerData(storageDataID);
return this.getAllChallengeLevelStatus(storageDataID, level.getWorld()).stream().
filter(LevelStatus::isUnlocked).
anyMatch(lv -> lv.getLevel().equals(level));
return this.islandWorldManager.getAddon(world).filter(gameMode ->
this.getAllChallengeLevelStatus(storageDataID, gameMode.getDescription().getName()).
stream().
filter(LevelStatus::isUnlocked).
anyMatch(lv -> lv.getLevel().equals(level))).
isPresent();
}
@ -1034,21 +1065,22 @@ public class ChallengesManager
*/
public void resetAllChallenges(UUID userID, World world, UUID adminID)
{
world = Util.getWorld(world);
String storageID = this.getDataUniqueID(userID, world);
String storageID = this.getDataUniqueID(userID, Util.getWorld(world));
this.resetAllChallenges(storageID, world.getName());
this.addLogEntry(storageID, new LogEntry.Builder("RESET_ALL").
data("user-id", userID.toString()).
data("admin-id", adminID == null ? "ISLAND_RESET" : adminID.toString()).
build());
this.islandWorldManager.getAddon(world).ifPresent(gameMode -> {
this.resetAllChallenges(storageID, gameMode.getDescription().getName());
this.addLogEntry(storageID, new LogEntry.Builder("RESET_ALL").
data("user-id", userID.toString()).
data("admin-id", adminID == null ? "ISLAND_RESET" : adminID.toString()).
build());
// Fire event that admin resets user challenge
Bukkit.getServer().getPluginManager().callEvent(
new ChallengeResetAllEvent(world.getName(),
userID,
adminID != null,
adminID == null ? "ISLAND_RESET" : "RESET_ALL"));
// Fire event that admin resets user challenge
Bukkit.getServer().getPluginManager().callEvent(
new ChallengeResetAllEvent(gameMode.getDescription().getName(),
userID,
adminID != null,
adminID == null ? "ISLAND_RESET" : "RESET_ALL"));
});
}
@ -1089,7 +1121,7 @@ public class ChallengesManager
*/
public boolean isLevelUnlocked(User user, World world, ChallengeLevel level)
{
return this.isLevelUnlocked(this.getDataUniqueID(user, Util.getWorld(world)), level);
return this.isLevelUnlocked(this.getDataUniqueID(user, Util.getWorld(world)), world, level);
}
@ -1129,19 +1161,6 @@ public class ChallengesManager
}
/**
* This method returns LevelStatus object for given challenge level.
* @param world World where level must be validated.
* @param level Level that must be validated.
* @param user User who need to be validated.
* @return LevelStatus of given level.
*/
public LevelStatus getChallengeLevelStatus(User user, World world, ChallengeLevel level)
{
return this.getChallengeLevelStatus(this.getDataUniqueID(user, Util.getWorld(world)), level);
}
/**
* This method returns LevelStatus object for given challenge level.
* @param world World where level must be validated.
@ -1151,7 +1170,7 @@ public class ChallengesManager
*/
public LevelStatus getChallengeLevelStatus(UUID user, World world, ChallengeLevel level)
{
return this.getChallengeLevelStatus(this.getDataUniqueID(user, Util.getWorld(world)), level);
return this.getChallengeLevelStatus(this.getDataUniqueID(user, Util.getWorld(world)), world, level);
}
@ -1164,8 +1183,11 @@ public class ChallengesManager
*/
public List<LevelStatus> getAllChallengeLevelStatus(User user, World world)
{
world = Util.getWorld(world);
return this.getAllChallengeLevelStatus(this.getDataUniqueID(user, world), world.getName());
return this.islandWorldManager.getAddon(world).map(gameMode ->
this.getAllChallengeLevelStatus(
this.getDataUniqueID(user, Util.getWorld(world)),
gameMode.getDescription().getName())).
orElse(Collections.emptyList());
}
@ -1182,19 +1204,13 @@ public class ChallengesManager
*/
public List<String> getAllChallengesNames(@NonNull World world)
{
World gameWorld = Util.getWorld(world);
if (gameWorld == null)
{
return Collections.emptyList();
}
// TODO: Probably need to check also database.
return this.challengeCacheData.values().stream().
return this.islandWorldManager.getAddon(world).map(gameMode ->
this.challengeCacheData.values().stream().
sorted(Comparator.comparing(Challenge::getOrder)).
filter(challenge -> challenge.matchWorld(gameWorld.getName())).
filter(challenge -> challenge.matchGameMode(gameMode.getDescription().getName())).
map(Challenge::getUniqueId).
collect(Collectors.toList());
collect(Collectors.toList())).
orElse(Collections.emptyList());
}
@ -1206,18 +1222,12 @@ public class ChallengesManager
*/
public List<Challenge> getAllChallenges(@NonNull World world)
{
World gameWorld = Util.getWorld(world);
if (gameWorld == null)
{
return Collections.emptyList();
}
// TODO: Probably need to check also database.
return this.challengeCacheData.values().stream().
filter(challenge -> challenge.matchWorld(gameWorld.getName())).
return this.islandWorldManager.getAddon(world).map(gameMode ->
this.challengeCacheData.values().stream().
sorted(Comparator.comparing(Challenge::getOrder)).
collect(Collectors.toList());
filter(challenge -> challenge.matchGameMode(gameMode.getDescription().getName())).
collect(Collectors.toList())).
orElse(Collections.emptyList());
}
@ -1372,28 +1382,23 @@ public class ChallengesManager
*/
public List<ChallengeLevel> getLevels(@NonNull World world)
{
world = Util.getWorld(world);
if (world == null)
{
return Collections.emptyList();
}
return this.getLevels(world.getName());
return this.islandWorldManager.getAddon(world).map(gameMode ->
this.getLevels(gameMode.getDescription().getName())).
orElse(Collections.emptyList());
}
/**
* This method returns list of challenge levels in given world.
* @param world for which levels must be searched.
* @return List with challenges in given world.
* This method returns list of challenge levels in given gameMode.
* @param gameMode for which levels must be searched.
* @return List with challengeLevel in given gameMode.
*/
public List<ChallengeLevel> getLevels(String world)
private List<ChallengeLevel> getLevels(String gameMode)
{
// TODO: Probably need to check also database.
return this.levelCacheData.values().stream().
sorted(ChallengeLevel::compareTo).
filter(level -> level.matchWorld(world)).
filter(level -> level.matchGameMode(gameMode)).
collect(Collectors.toList());
}
@ -1595,27 +1600,21 @@ public class ChallengesManager
*/
public boolean hasAnyChallengeData(@NonNull World world)
{
world = Util.getWorld(world);
if (world == null)
{
return false;
}
return this.hasAnyChallengeData(world.getName());
return this.islandWorldManager.getAddon(world).filter(gameMode ->
this.hasAnyChallengeData(gameMode.getDescription().getName())).isPresent();
}
/**
* This method returns if in given world has any stored challenge or level.
* @param worldName World name that needs to be checked
* @return <code>true</code> if world has any challenge or level, otherwise <code>false</code>
* This method returns if in given gameMode has any stored challenge or level.
* @param gameMode GameMode addon name that needs to be checked
* @return <code>true</code> if gameMode has any challenge or level, otherwise <code>false</code>
*/
public boolean hasAnyChallengeData(@NonNull String worldName)
public boolean hasAnyChallengeData(@NonNull String gameMode)
{
return this.challengeDatabase.loadObjects().stream().anyMatch(
challenge -> challenge.matchWorld(worldName)) ||
challenge -> challenge.matchGameMode(gameMode)) ||
this.levelDatabase.loadObjects().stream().anyMatch(
level -> level.matchWorld(worldName));
level -> level.matchGameMode(gameMode));
}
}

View File

@ -11,6 +11,7 @@ import world.bentobox.bentobox.util.Util;
import world.bentobox.challenges.ChallengesAddon;
import world.bentobox.challenges.database.object.Challenge;
import world.bentobox.challenges.tasks.TryToComplete;
import world.bentobox.challenges.utils.Utils;
/**
@ -58,7 +59,7 @@ public class CompleteChallengeCommand extends CompositeCommand
else if (!args.get(0).isEmpty())
{
// Add world name back at the start
String challengeName = Util.getWorld(this.getWorld()).getName() + "_" + args.get(0);
String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(0);
Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName);
int count = args.size() == 2 ? Integer.valueOf(args.get(1)) : 1;
@ -102,7 +103,7 @@ public class CompleteChallengeCommand extends CompositeCommand
case 3:
// Create suggestions with all challenges that is available for users.
returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream().
map(challenge -> challenge.substring(Util.getWorld(this.getWorld()).getName().length() + 1)).
map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).
collect(Collectors.toList()));
break;

View File

@ -11,7 +11,7 @@ import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
import world.bentobox.challenges.ChallengesAddon;
import world.bentobox.challenges.database.object.Challenge;
import world.bentobox.challenges.tasks.TryToComplete;
import world.bentobox.challenges.utils.Utils;
/**
@ -92,7 +92,7 @@ public class CompleteCommand extends CompositeCommand
}
// Add world name back at the start
String challengeName = Util.getWorld(this.getWorld()).getName() + "_" + args.get(1);
String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(1);
Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName);
if (challenge != null)
@ -169,7 +169,7 @@ public class CompleteCommand extends CompositeCommand
case 4:
// Create suggestions with all challenges that is available for users.
returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream().
map(challenge -> challenge.substring(Util.getWorld(this.getWorld()).getName().length() + 1)).
map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).
collect(Collectors.toList()));
break;

View File

@ -11,7 +11,7 @@ import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
import world.bentobox.challenges.ChallengesAddon;
import world.bentobox.challenges.database.object.Challenge;
import world.bentobox.challenges.tasks.TryToComplete;
import world.bentobox.challenges.utils.Utils;
/**
@ -110,7 +110,7 @@ public class ResetCommand extends CompositeCommand
}
else
{
String challengeName = Util.getWorld(this.getWorld()).getName() + "_" + args.get(1);
String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(1);
Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName);
if (challenge != null)
@ -187,7 +187,7 @@ public class ResetCommand extends CompositeCommand
case 4:
// Create suggestions with all challenges that is available for users.
returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream().
map(challenge -> challenge.substring(Util.getWorld(this.getWorld()).getName().length() + 1)).
map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).
collect(Collectors.toList()));
returnList.add("all");

View File

@ -979,16 +979,18 @@ public class Challenge implements DataObject
/**
* This method match if given worldName relates to current challenge. It is detected
* via challenge uniqueId, as it always must start with world name.
* This method match if given gameMode relates to current challenge. It is detected
* via challenge uniqueId, as it always must start with gameMode name.
* This method is created to avoid issues with capital letters in world names in 1.14
* @param worldName Name that must be checked.
* @return {@code true} if current challenge relates to given world name, otherwise
* and readjust to store GameMode name instead of world name.
* @param gameMode Name that must be checked.
* @return {@code true} if current challenge relates to given gameMode name, otherwise
* {@code false}.
*/
public boolean matchWorld(String worldName)
public boolean matchGameMode(String gameMode)
{
return this.uniqueId.regionMatches(true, 0, worldName, 0, worldName.length());
return gameMode != null &&
this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length());
}

View File

@ -426,16 +426,19 @@ public class ChallengeLevel implements DataObject, Comparable<ChallengeLevel>
/**
* This method match if given worldName relates to current level. It is detected
* via level uniqueId, as it always must start with world name.
* This method is created to avoid issues with capital letters in world names in 1.14
* @param worldName Name that must be checked.
* @return {@code true} if current level relates to given world name, otherwise
* This method match if given gameMode relates to current level. It is detected
* via level uniqueId, as it always must start with gameMode name.
*
* This method is created to avoid issues with capital letters in world names in 1.14.
* It is reused for GameMode storage change.
* @param gameMode Name that must be checked.
* @return {@code true} if current level relates to given gameMode name, otherwise
* {@code false}.
*/
public boolean matchWorld(String worldName)
public boolean matchGameMode(String gameMode)
{
return this.uniqueId.regionMatches(true, 0, worldName, 0, worldName.length());
return gameMode != null &&
this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length());
}

View File

@ -197,15 +197,15 @@ public class ChallengesPlayerData implements DataObject
/**
* Resets all challenges and levels in world for this player
* Resets all challenges and levels in GameMode for this player
*
* @param worldName world which challenges must be reset.
* @param gameMode GameMode which challenges must be reset.
*/
public void reset(@NonNull String worldName)
public void reset(@NonNull String gameMode)
{
challengeStatus.keySet().removeIf(n -> n.regionMatches(true, 0, worldName, 0, worldName.length()));
challengesTimestamp.keySet().removeIf(n -> n.regionMatches(true, 0, worldName, 0, worldName.length()));
levelsDone.removeIf(n -> n.regionMatches(true, 0, worldName, 0, worldName.length()));
challengeStatus.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length()));
challengesTimestamp.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length()));
levelsDone.removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length()));
}

View File

@ -5,7 +5,10 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import java.util.Optional;
import net.wesjd.anvilgui.AnvilGUI;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
@ -15,6 +18,7 @@ import world.bentobox.challenges.ChallengesAddon;
import world.bentobox.challenges.panel.CommonGUI;
import world.bentobox.challenges.panel.util.ConfirmationGUI;
import world.bentobox.challenges.utils.GuiUtils;
import world.bentobox.challenges.utils.Utils;
/**
@ -208,7 +212,7 @@ public class AdminGUI extends CommonGUI
this.user.getPlayer(),
"unique_id",
(player, reply) -> {
String newName = Util.getWorld(this.world).getName() + "_" + reply;
String newName = Utils.getGameMode(this.world) + "_" + reply;
if (!this.addon.getChallengesManager().containsChallenge(newName))
{
@ -244,7 +248,7 @@ public class AdminGUI extends CommonGUI
this.user.getPlayer(),
"unique_id",
(player, reply) -> {
String newName = Util.getWorld(this.world).getName() + "_" + reply;
String newName = Utils.getGameMode(this.world) + "_" + reply;
if (!this.addon.getChallengesManager().containsLevel(newName))
{

View File

@ -516,7 +516,7 @@ public class TryToComplete
result = EMPTY_RESULT;
}
else if (Util.getWorld(this.world) != Util.getWorld(this.user.getWorld()) ||
!this.challenge.matchWorld(Util.getWorld(this.world).getName()))
!this.challenge.matchGameMode(Utils.getGameMode(this.world)))
{
this.user.sendMessage("general.errors.wrong-world");
result = EMPTY_RESULT;

View File

@ -2,10 +2,13 @@ package world.bentobox.challenges.utils;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import world.bentobox.bentobox.BentoBox;
/**
* Util methods used in different situations.
@ -78,4 +81,18 @@ public class Utils
material.equals(Material.WRITTEN_BOOK) ||
material.equals(Material.FILLED_MAP);
}
/**
* This method transforms given World into GameMode name. If world is not a GameMode
* world then it returns null.
* @param world World which gameMode name must be found out.
* @return GameMode name or null.
*/
public static String getGameMode(World world)
{
return BentoBox.getInstance().getIWM().getAddon(world).
map(gameModeAddon -> gameModeAddon.getDescription().getName()).
orElse(null);
}
}