Add ability to migrate challenges from 0.5.0 - 0.7.5 data storage mode to new 0.8.0 format.

Part of implementing #105
This commit is contained in:
BONNe1704 2019-08-02 12:22:52 +03:00
parent 3985efa4d0
commit 76fb30be36
5 changed files with 237 additions and 1 deletions

View File

@ -23,6 +23,7 @@ import world.bentobox.challenges.events.ChallengeResetAllEvent;
import world.bentobox.challenges.events.ChallengeResetEvent;
import world.bentobox.challenges.events.LevelCompletedEvent;
import world.bentobox.challenges.utils.LevelStatus;
import world.bentobox.challenges.utils.Utils;
/**
@ -148,6 +149,7 @@ public class ChallengesManager
this.challengeDatabase.loadObjects().forEach(this::loadChallenge);
this.levelDatabase.loadObjects().forEach(this::loadLevel);
// It is not necessary to load all players in memory.
// this.playersDatabase.loadObjects().forEach(this::loadPlayerData);
}
@ -494,6 +496,180 @@ public class ChallengesManager
}
// ---------------------------------------------------------------------
// Section: Wipe data
// ---------------------------------------------------------------------
/**
* This method migrated all challenges addon data from worldName to addonID formant.
*/
public void migrateDatabase(User user, World world)
{
world = Util.getWorld(world);
if (user.isPlayer())
{
user.sendMessage("challenges.messages.admin.migrate-start");
}
else
{
this.addon.log("Starting migration to new data format.");
}
boolean challenges = this.migrateChallenges(world);
boolean levels = this.migrateLevels(world);
if (challenges || levels)
{
this.migratePlayers(world);
if (user.isPlayer())
{
user.sendMessage("challenges.messages.admin.migrate-end");
}
else
{
this.addon.log("Migration to new data format completed.");
}
}
else
{
if (user.isPlayer())
{
user.sendMessage("challenges.messages.admin.migrate-not");
}
else
{
this.addon.log("All data is valid. Migration is not necessary.");
}
}
}
/**
* This method collects all data from levels database and migrates them.
*/
private boolean migrateLevels(World world)
{
String addonName = Utils.getGameMode(world);
if (addonName == null || addonName.equalsIgnoreCase(world.getName()))
{
return false;
}
boolean updated = false;
List<ChallengeLevel> levelList = this.levelDatabase.loadObjects();
for (ChallengeLevel level : levelList)
{
if (level.getUniqueId().regionMatches(true, 0, world.getName() + "_", 0, world.getName().length() + 1))
{
this.levelDatabase.deleteID(level.getUniqueId());
this.levelCacheData.remove(level.getUniqueId());
level.setUniqueId(
addonName + level.getUniqueId().substring(world.getName().length()));
Set<String> challengesID = new HashSet<>(level.getChallenges());
level.getChallenges().clear();
challengesID.forEach(challenge ->
level.getChallenges().add(addonName + challenge.substring(world.getName().length())));
this.levelDatabase.saveObject(level);
this.levelCacheData.put(level.getUniqueId(), level);
updated = true;
}
}
return updated;
}
/**
* This method collects all data from challenges database and migrates them.
*/
private boolean migrateChallenges(World world)
{
String addonName = Utils.getGameMode(world);
if (addonName == null || addonName.equalsIgnoreCase(world.getName()))
{
return false;
}
boolean updated = false;
List<Challenge> challengeList = this.challengeDatabase.loadObjects();
for (Challenge challenge : challengeList)
{
if (challenge.getUniqueId().regionMatches(true, 0, world.getName() + "_", 0, world.getName().length() + 1))
{
this.challengeDatabase.deleteID(challenge.getUniqueId());
this.challengeCacheData.remove(challenge.getUniqueId());
challenge.setUniqueId(addonName + challenge.getUniqueId().substring(world.getName().length()));
updated = true;
this.challengeDatabase.saveObject(challenge);
this.challengeCacheData.put(challenge.getUniqueId(), challenge);
}
}
return updated;
}
/**
* This method collects all data from players database and migrates them.
*/
private void migratePlayers(World world)
{
String addonName = Utils.getGameMode(world);
if (addonName == null || addonName.equalsIgnoreCase(world.getName()))
{
return;
}
List<ChallengesPlayerData> playerDataList = this.playersDatabase.loadObjects();
playerDataList.forEach(playerData -> {
Set<String> levelsDone = new TreeSet<>(playerData.getLevelsDone());
levelsDone.forEach(level -> {
if (level.regionMatches(true, 0, world.getName() + "_", 0, world.getName().length() + 1))
{
playerData.getLevelsDone().remove(level);
playerData.getLevelsDone().add(addonName + level.substring(world.getName().length()));
}
});
Map<String, Integer> challengeStatus = new TreeMap<>(playerData.getChallengeStatus());
challengeStatus.forEach((challenge, count) -> {
if (challenge.regionMatches(true, 0, world.getName() + "_", 0, world.getName().length() + 1))
{
playerData.getChallengeStatus().remove(challenge);
playerData.getChallengeStatus().put(addonName + challenge.substring(world.getName().length()), count);
}
});
Map<String, Long> challengeTimestamp = new TreeMap<>(playerData.getChallengesTimestamp());
challengeTimestamp.forEach((challenge, timestamp) -> {
if (challenge.regionMatches(true, 0, world.getName() + "_", 0, world.getName().length() + 1))
{
playerData.getChallengesTimestamp().remove(challenge);
playerData.getChallengesTimestamp().put(addonName + challenge.substring(world.getName().length()), timestamp);
}
});
this.playersDatabase.saveObject(playerData);
});
}
// ---------------------------------------------------------------------
// Section: Saving methods
// ---------------------------------------------------------------------

View File

@ -43,6 +43,10 @@ public class Challenges extends CompositeCommand
// Reset challenge command
new ResetCommand(this.getAddon(), this);
new ShowChallenges(this.getAddon(), this);
new MigrateCommand(this.getAddon(), this);
}

View File

@ -0,0 +1,37 @@
package world.bentobox.challenges.commands.admin;
import java.util.List;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.challenges.ChallengesAddon;
public class MigrateCommand extends CompositeCommand {
/**
* Migrates challenges
* @param addon
* @param cmd
*/
public MigrateCommand(Addon addon, CompositeCommand cmd) {
super(addon, cmd, "migrate");
}
@Override
public boolean execute(User user, String label, List<String> args) {
((ChallengesAddon)getAddon()).getChallengesManager().migrateDatabase(user, getWorld());
return true;
}
@Override
public void setup() {
this.setPermission("challenges.admin");
this.setParametersHelp("challenges.commands.admin.migrate.parameters");
this.setDescription("challenges.commands.admin.migrate.description");
}
}

View File

@ -1,6 +1,7 @@
package world.bentobox.challenges.commands.admin;
import java.util.List;
import java.util.logging.Level;
import world.bentobox.challenges.ChallengesAddon;
import world.bentobox.bentobox.api.addons.Addon;
@ -28,8 +29,19 @@ public class ShowChallenges extends CompositeCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
((ChallengesAddon)getAddon()).getChallengesManager().getAllChallengesNames(this.getWorld()).forEach(user::sendRawMessage);
if (user.isPlayer())
{
((ChallengesAddon) getAddon()).getChallengesManager().
getAllChallengesNames(this.getWorld()).forEach(user::sendRawMessage);
}
else
{
((ChallengesAddon) getAddon()).getChallengesManager().
getAllChallengesNames(this.getWorld()).forEach(c -> this.getAddon().log(c));
}
return true;
}
}

View File

@ -36,6 +36,9 @@ challenges:
reset:
description: 'This command allows to reset challenge for player without GUI. If "challenge_id" is set to "all", then it will reset all challenges.'
parameters: '<player> <challenge_id>'
migrate:
description: 'This method allows to migrate challenges data that refers to current game mode world to new 0.8.0 storage format.'
parameters: ''
user:
main:
description: 'This method opens Challenges GUI.'
@ -341,6 +344,10 @@ challenges:
reset: '&2You reset challenge [name] for [player]!'
reset-all: '&2All [player] challenges were reset!'
not-completed: '&2This challenge is not completed yet!'
migrate-start: '&2Start migrating challenges addon data.'
migrate-end: '&2Challenges addon data is updated to new format.'
migrate-not: '&2All data is valid.'
you-completed-challenge: '&2You completed the [value] &r&2challenge!'
you-repeated-challenge: '&2You repeated the [value] &r&2challenge!'
you-repeated-challenge-multiple: '&2You repeated the [value] &r&2challenge [count] times!'