Handles null flags in island object when loading from database

Flags can become unknown to BentoBox due to an addon no longer being
installed, or a flag being removed from the code. When this happens and
an island object is loaded, the map of flags could contain multiple null
keys, which is illegal and therefore the island would not load. (A map
can only have one null key). A change back in February was also
deregistering deprecated flags, so any old databases may have already
had one null flag in them.

This change will convert any unknown or null flags into a temporary flag
with a known ID that starts with NULL_FLAG_ followed by a UUID. This
enables the object to be loaded by GSON successfully. Immediately after
loading, any flags that start with NULL_FLAG are removed, thus cleaning
up the flag map.

Addresses
https://github.com/BentoBoxWorld/BentoBox/issues/680
This commit is contained in:
tastybento 2019-05-21 00:15:58 -07:00
parent 29193cbc3a
commit 3a34133d1a
3 changed files with 24 additions and 11 deletions

View File

@ -1,6 +1,9 @@
package world.bentobox.bentobox.database.json.adapters;
import java.io.IOException;
import java.util.UUID;
import org.bukkit.Material;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
@ -34,6 +37,13 @@ public class FlagTypeAdapter extends TypeAdapter<Flag> {
reader.nextNull();
return null;
}
return plugin.getFlagsManager().getFlag(reader.nextString()).orElse(null);
String id = reader.nextString();
Flag f = plugin.getFlagsManager().getFlag(id).orElse(null);
// Flags can end up null if an addon that created one is removed or if a flag name was changed
if (f == null) {
// Create a temporary flag with a unique key. It will be immediately deleted after loading
f = new Flag.Builder("NULL_FLAG_"+ UUID.randomUUID().toString(), Material.STONE).build();
}
return f;
}
}

View File

@ -412,26 +412,25 @@ public final class Flags {
/**
* Protects against visitors dying stuff, like sheep or signs
*
*
* @since 1.5.0
* @see DyeListener
*/
public static final Flag DYE = new Flag.Builder("DYE", Material.LIGHT_BLUE_DYE).type(Type.PROTECTION).listener(new DyeListener()).build();
/**
* Provides a list of all the Flag instances contained in this class using reflection.
* @return List of all the flags in this class
*/
public static List<Flag> values() {
return Arrays.stream(Flags.class.getFields())
.filter(field -> field.getAnnotation(Deprecated.class) == null) // Ensures it is not deprecated
.map(field -> {
try {
return (Flag)field.get(null);
} catch (IllegalArgumentException | IllegalAccessException e) {
Bukkit.getLogger().severe("Could not get Flag values " + e.getMessage());
}
return null;
}).collect(Collectors.toList());
try {
return (Flag)field.get(null);
} catch (IllegalArgumentException | IllegalAccessException e) {
Bukkit.getLogger().severe("Could not get Flag values " + e.getMessage());
}
return null;
}).collect(Collectors.toList());
}
}

View File

@ -774,6 +774,10 @@ public class IslandsManager {
} else if (island.isSpawn()) {
// Success, set spawn if this is the spawn island.
this.setSpawn(island);
} else {
// Successful load
// Clean any null flags out of the island - these can occur for various reasons
island.getFlags().keySet().removeIf(f -> f.getID().startsWith("NULL_FLAG"));
}
}