Skip to content

Commit

Permalink
Add ability to migrate challenges from 0.5.0 - 0.7.5 data storage mod…
Browse files Browse the repository at this point in the history
…e to new 0.8.0 format.

Part of implementing #105
  • Loading branch information
BONNe committed Aug 2, 2019
1 parent 3985efa commit 76fb30b
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 1 deletion.
176 changes: 176 additions & 0 deletions src/main/java/world/bentobox/challenges/ChallengesManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import world.bentobox.challenges.events.ChallengeResetEvent;
import world.bentobox.challenges.events.LevelCompletedEvent;
import world.bentobox.challenges.utils.LevelStatus;
import world.bentobox.challenges.utils.Utils;


/**
Expand Down Expand Up @@ -148,6 +149,7 @@ public void load()

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);
}
Expand Down Expand Up @@ -494,6 +496,180 @@ private void wipePlayers()
}


// ---------------------------------------------------------------------
// 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
// ---------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public void setup()

// Reset challenge command
new ResetCommand(this.getAddon(), this);

new ShowChallenges(this.getAddon(), this);

new MigrateCommand(this.getAddon(), this);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -28,8 +29,19 @@ public void setup() {

@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;

}

}
7 changes: 7 additions & 0 deletions src/main/resources/locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
Expand Down Expand Up @@ -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!'
Expand Down

0 comments on commit 76fb30b

Please sign in to comment.