Implements adding unknown default island flag values in settings. (#2001)

* Implements option to define non-existing flags in WorldSettings.

This change adds 2 new methods in WorldSettings:
* WorldSettings#getDefaultIslandFlagNames
* WorldSettings#getDefaultIslandSettingNames

These methods replace getDefaultIslandFlags and getDefaultIslandSettings methods.
Default implementation just reads values from replaced methods.

Fixes #1830

* Implement conversion from flag id to actual flag object.

Replaces flag assignment to new island based on flag id's.

Fixes #1830

* Switch from Flag object to String object in Island class.

This switch allows to keep flags that are not present in current BentoBox installation. Otherwise, unknown flags may cause an issues.

Fixes #1830

* Implement FlagBooleanSerializer.

This serializer converts input map of String, Boolean to map of String, Integer.

This map is used to read island setting flags, and integer value is not classic boolean values. (0 for true and -1 for false).

Fixes #1830
This commit is contained in:
BONNe 2022-07-06 20:20:08 +03:00 committed by GitHub
parent d8fa029ac9
commit db323390cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 167 additions and 38 deletions

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.api.configuration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -31,11 +32,54 @@ public interface WorldSettings extends ConfigObject {
/**
* @return default rank settings for new islands
* @deprecated since 1.21
* Map of Flag, Integer does not allow to load other plugin/addon flags.
* It cannot be replaced with Map of String, Integer due to compatibility issues.
* @see WorldSettings#getDefaultIslandFlagNames()
*/
@Deprecated
Map<Flag, Integer> getDefaultIslandFlags();
/**
* Return map of flags ID's linked to default rank for new island.
* This is necessary so users could specify any flag names in settings file from other plugins and addons.
* Otherwise, Flag reader would mark flag as invalid and remove it.
* Default implementation is compatibility layer so GameModes that are not upgraded still works.
* @since 1.21
* @return default rank settings for new islands.
*/
default Map<String, Integer> getDefaultIslandFlagNames()
{
Map<String, Integer> flags = new HashMap<>();
this.getDefaultIslandFlags().forEach((key, value) -> flags.put(key.getID(), value));
return flags;
}
/**
* @return default settings for new
* @deprecated since 1.21
* Map of Flag, Integer does not allow to load other plugin/addon flags.
* It cannot be replaced with Map of String, Integer due to compatibility issues.
* @see WorldSettings#getDefaultIslandSettingNames()
*/
@Deprecated
Map<Flag, Integer> getDefaultIslandSettings();
/**
* Return map of flags ID's linked to default settings for new island.
* This is necessary so users could specify any flag names in settings file from other plugins and addons.
* Otherwise, Flag reader would mark flag as invalid and remove it.
* Default implementation is compatibility layer so GameModes that are not upgraded still works.
* @since 1.21
* @return default settings for new islands.
*/
default Map<String, Integer> getDefaultIslandSettingNames()
{
Map<String, Integer> flags = new HashMap<>();
this.getDefaultIslandSettings().forEach((key, value) -> flags.put(key.getID(), value));
return flags;
}
/**
* Get the world difficulty
* @return difficulty

View File

@ -39,8 +39,6 @@ import world.bentobox.bentobox.api.metadata.MetaDataAble;
import world.bentobox.bentobox.api.metadata.MetaDataValue;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.adapters.Adapter;
import world.bentobox.bentobox.database.objects.adapters.FlagSerializer;
import world.bentobox.bentobox.database.objects.adapters.FlagSerializer3;
import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.RanksManager;
@ -159,9 +157,8 @@ public class Island implements DataObject, MetaDataAble {
private boolean purgeProtected = false;
//// Protection flags ////
@Adapter(FlagSerializer.class)
@Expose
private Map<Flag, Integer> flags = new HashMap<>();
private Map<String, Integer> flags = new HashMap<>();
//// Island History ////
@Adapter(LogEntryListAdapter.class)
@ -180,9 +177,8 @@ public class Island implements DataObject, MetaDataAble {
/**
* Used to store flag cooldowns for this island
*/
@Adapter(FlagSerializer3.class)
@Expose
private Map<Flag, Long> cooldowns = new HashMap<>();
private Map<String, Long> cooldowns = new HashMap<>();
/**
* Commands and the rank required to use them for this island
@ -368,13 +364,13 @@ public class Island implements DataObject, MetaDataAble {
* @return flag value
*/
public int getFlag(@NonNull Flag flag) {
return flags.computeIfAbsent(flag, k -> flag.getDefaultRank());
return flags.computeIfAbsent(flag.getID(), k -> flag.getDefaultRank());
}
/**
* @return the flags
*/
public Map<Flag, Integer> getFlags() {
public Map<String, Integer> getFlags() {
return flags;
}
@ -722,9 +718,7 @@ public class Island implements DataObject, MetaDataAble {
*/
@NonNull
public List<Player> getVisitors() {
return Bukkit.getOnlinePlayers().stream()
.filter(player -> playerIsVisitor(player))
.collect(Collectors.toList());
return Bukkit.getOnlinePlayers().stream().filter(this::playerIsVisitor).collect(Collectors.toList());
}
/**
@ -736,7 +730,7 @@ public class Island implements DataObject, MetaDataAble {
* @see #getVisitors()
*/
public boolean hasVisitors() {
return Bukkit.getOnlinePlayers().stream().anyMatch(player -> playerIsVisitor(player));
return Bukkit.getOnlinePlayers().stream().anyMatch(this::playerIsVisitor);
}
/**
@ -865,7 +859,7 @@ public class Island implements DataObject, MetaDataAble {
* @param doSubflags - whether to set subflags
*/
public void setFlag(Flag flag, int value, boolean doSubflags) {
flags.put(flag, value);
flags.put(flag.getID(), value);
// Subflag support
if (doSubflags && flag.hasSubflags()) {
// Ensure that a subflag isn't a subflag of itself or else we're in trouble!
@ -877,7 +871,7 @@ public class Island implements DataObject, MetaDataAble {
/**
* @param flags the flags to set
*/
public void setFlags(Map<Flag, Integer> flags) {
public void setFlags(Map<String, Integer> flags) {
this.flags = flags;
setChanged();
}
@ -888,11 +882,13 @@ public class Island implements DataObject, MetaDataAble {
*/
public void setFlagsDefaults() {
BentoBox plugin = BentoBox.getInstance();
Map<Flag, Integer> result = new HashMap<>();
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.PROTECTION))
.forEach(f -> result.put(f, plugin.getIWM().getDefaultIslandFlags(world).getOrDefault(f, f.getDefaultRank())));
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.SETTING))
.forEach(f -> result.put(f, plugin.getIWM().getDefaultIslandSettings(world).getOrDefault(f, f.getDefaultRank())));
Map<String, Integer> result = new HashMap<>();
plugin.getFlagsManager().getFlags().stream().
filter(f -> f.getType().equals(Flag.Type.PROTECTION)).
forEach(f -> result.put(f.getID(), plugin.getIWM().getDefaultIslandFlags(world).getOrDefault(f, f.getDefaultRank())));
plugin.getFlagsManager().getFlags().stream().
filter(f -> f.getType().equals(Flag.Type.SETTING)).
forEach(f -> result.put(f.getID(), plugin.getIWM().getDefaultIslandSettings(world).getOrDefault(f, f.getDefaultRank())));
this.setFlags(result);
setChanged();
}
@ -1134,7 +1130,7 @@ public class Island implements DataObject, MetaDataAble {
public void setSettingsFlag(Flag flag, boolean state, boolean doSubflags) {
int newState = state ? 1 : -1;
if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) {
flags.put(flag, newState);
flags.put(flag.getID(), newState);
if (doSubflags && flag.hasSubflags()) {
// If we have circular subflags or a flag is a subflag of itself we are in trouble!
flag.getSubflags().forEach(subflag -> setSettingsFlag(subflag, state, true));
@ -1275,10 +1271,10 @@ public class Island implements DataObject, MetaDataAble {
* @since 1.6.0
*/
public boolean isCooldown(Flag flag) {
if (cooldowns.containsKey(flag) && cooldowns.get(flag) > System.currentTimeMillis()) {
if (cooldowns.containsKey(flag.getID()) && cooldowns.get(flag.getID()) > System.currentTimeMillis()) {
return true;
}
cooldowns.remove(flag);
cooldowns.remove(flag.getID());
setChanged();
return false;
}
@ -1288,21 +1284,21 @@ public class Island implements DataObject, MetaDataAble {
* @param flag - Flag to cooldown
*/
public void setCooldown(Flag flag) {
cooldowns.put(flag, flag.getCooldown() * 1000L + System.currentTimeMillis());
cooldowns.put(flag.getID(), flag.getCooldown() * 1000L + System.currentTimeMillis());
setChanged();
}
/**
* @return the cooldowns
*/
public Map<Flag, Long> getCooldowns() {
public Map<String, Long> getCooldowns() {
return cooldowns;
}
/**
* @param cooldowns the cooldowns to set
*/
public void setCooldowns(Map<Flag, Long> cooldowns) {
public void setCooldowns(Map<String, Long> cooldowns) {
this.cooldowns = cooldowns;
setChanged();
}

View File

@ -0,0 +1,65 @@
package world.bentobox.bentobox.database.objects.adapters;
import org.bukkit.configuration.MemorySection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* This Serializer migrates Map of String, Boolean to Map of String, Integer in serialization process.
* It is necessary because current implementation requires flags to be mapped to Integer value.
* @author BONNe
*/
public class FlagBooleanSerializer implements AdapterInterface<Map<String, Integer>, Map<String, Boolean>>
{
@SuppressWarnings("unchecked")
@Override
public Map<String, Integer> deserialize(Object object)
{
Map<String, Integer> result = new HashMap<>();
if (object == null)
{
return result;
}
// For YAML
if (object instanceof MemorySection section)
{
for (String key : section.getKeys(false))
{
result.put(key, section.getBoolean(key) ? 0 : -1);
}
}
else
{
for (Entry<String, Boolean> en : ((Map<String, Boolean>) object).entrySet())
{
result.put(en.getKey(), en.getValue() ? 0 : -1);
}
}
return result;
}
@SuppressWarnings("unchecked")
@Override
public Map<String, Boolean> serialize(Object object)
{
Map<String, Boolean> result = new HashMap<>();
if (object == null)
{
return result;
}
Map<String, Integer> flags = (Map<String, Integer>) object;
for (Entry<String, Integer> en : flags.entrySet())
{
result.put(en.getKey(), en.getValue() >= 0);
}
return result;
}
}

View File

@ -171,10 +171,13 @@ public class IslandWorldManager {
}
// Set default island settings
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.PROTECTION))
.forEach(f -> settings.getDefaultIslandFlags().putIfAbsent(f, f.getDefaultRank()));
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.SETTING))
.forEach(f -> settings.getDefaultIslandSettings().putIfAbsent(f, f.getDefaultRank()));
plugin.getFlagsManager().getFlags().stream().
filter(f -> f.getType().equals(Flag.Type.PROTECTION)).
forEach(f -> settings.getDefaultIslandFlagNames().putIfAbsent(f.getID(), f.getDefaultRank()));
plugin.getFlagsManager().getFlags().stream().
filter(f -> f.getType().equals(Flag.Type.SETTING)).
forEach(f -> settings.getDefaultIslandSettingNames().putIfAbsent(f.getID(), f.getDefaultRank()));
Bukkit.getScheduler().runTask(plugin, () -> {
// Set world difficulty
Difficulty diff = settings.getDifficulty();
@ -477,7 +480,9 @@ public class IslandWorldManager {
* @return Friendly name or world name if world is not a game world
*/
public String getFriendlyName(@NonNull World world) {
return gameModes.containsKey(world) ? gameModes.get(world).getWorldSettings().getFriendlyName() : world.getName();
return gameModes.containsKey(world) ?
gameModes.get(world).getWorldSettings().getFriendlyName() :
world.getName();
}
/**
@ -699,8 +704,11 @@ public class IslandWorldManager {
* @param world - world
* @return default rank settings for new islands.
*/
public Map<Flag, Integer> getDefaultIslandFlags(@NonNull World world) {
return gameModes.containsKey(world) ? gameModes.get(world).getWorldSettings().getDefaultIslandFlags() : Collections.emptyMap();
public Map<Flag, Integer> getDefaultIslandFlags(@NonNull World world)
{
return this.gameModes.containsKey(world) ?
this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandFlagNames()) :
Collections.emptyMap();
}
/**
@ -715,12 +723,14 @@ public class IslandWorldManager {
/**
* Return island setting defaults for world
*
* @param world
* - world
* @param world - world
* @return default settings for new islands
*/
public Map<Flag, Integer> getDefaultIslandSettings(@NonNull World world) {
return gameModes.containsKey(world) ? gameModes.get(world).getWorldSettings().getDefaultIslandSettings() : Collections.emptyMap();
public Map<Flag, Integer> getDefaultIslandSettings(@NonNull World world)
{
return this.gameModes.containsKey(world) ?
this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandSettingNames()) :
Collections.emptyMap();
}
public boolean isUseOwnGenerator(@NonNull World world) {
@ -921,4 +931,18 @@ public class IslandWorldManager {
return gameModes.containsKey(world) && gameModes.get(world).getWorldSettings().isTeleportPlayerToIslandUponIslandCreation();
}
/**
* This method migrates Map of String, Integer to Map of Flag, Integer.
* @param flagNamesMap Map that contains flag names to their values.
* @return Flag objects to their values.
* @since 1.21
*/
private Map<Flag, Integer> convertToFlags(Map<String, Integer> flagNamesMap)
{
Map<Flag, Integer> flagMap = new HashMap<>();
flagNamesMap.forEach((key, value) ->
this.plugin.getFlagsManager().getFlag(key).ifPresent(flag -> flagMap.put(flag, value)));
return flagMap;
}
}

View File

@ -1333,7 +1333,7 @@ public class IslandsManager {
} 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"));
island.getFlags().keySet().removeIf(f -> f.startsWith("NULL_FLAG"));
}
}

View File

@ -1018,7 +1018,7 @@ public class IslandTest {
*/
@Test
public void testSetCooldowns() {
i.setCooldowns(Collections.singletonMap(Flags.BREAK_BLOCKS, 123L));
i.setCooldowns(Collections.singletonMap(Flags.BREAK_BLOCKS.getID(), 123L));
assertFalse(i.getCooldowns().isEmpty());
}