Skip to content

Commit

Permalink
Adds a persistent metadata API to User and Island classes.
Browse files Browse the repository at this point in the history
This is modeled after the Bukkit metadata API with the difference that
it is persistent, i.e., metadata is stored to the database. Metadata can
be placed on Islands or Users.

This API should be useful for addons that do not want or need to create
their own database tables and instead just want to tag the user with
some data, or tag the island with some data. It is intended for small
amounts of data, like boolean tags or other values.
  • Loading branch information
tastybento committed Dec 29, 2020
1 parent e84b1f1 commit d7c7559
Show file tree
Hide file tree
Showing 7 changed files with 449 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package world.bentobox.bentobox.api.metadata;

import java.util.Map;

import org.eclipse.jdt.annotation.NonNull;

/**
* This interface is for all BentoBox objects that have meta data
* @author tastybento
* @since 1.15.4
*/
public interface MetaDataAble {
/**
* @return the metaData
*/
public Map<String, MetaDataValue> getMetaData();

/**
* Get meta data by key
* @param key - key
* @return the value to which the specified key is mapped, or null if there is no mapping for the key
* @since 1.15.4
*/
public MetaDataValue getMetaData(@NonNull String key);

/**
* @param metaData the metaData to set
* @since 1.15.4
*/
public void setMetaData(Map<String, MetaDataValue> metaData);

/**
* Put a key, value string pair into the object's meta data
* @param key - key
* @param value - value
* @return the previous value associated with key, or null if there was no mapping for key.
* @since 1.15.4
*/
public MetaDataValue putMetaData(@NonNull String key, @NonNull MetaDataValue value);

/**
* Remove meta data
* @param key - key to remove
* @return the previous value associated with key, or null if there was no mapping for key.
* @since 1.15.4
*/
public MetaDataValue removeMetaData(@NonNull String key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package world.bentobox.bentobox.api.metadata;

import org.eclipse.jdt.annotation.NonNull;

import com.google.gson.annotations.Expose;

/**
* Stores meta data value in a GSON friendly way so it can be serialized and deserialized.
* Values that are null are not stored in the database, so only the appropriate type is stored.
* @author tastybento
* @since 1.15.4
*
*/
public class MetaDataValue {

// Use classes so null value is supported
@Expose
private Integer intValue;
@Expose
private Float floatValue;
@Expose
private Double doubleValue;
@Expose
private Long longValue;
@Expose
private Short shortValue;
@Expose
private Byte byteValue;
@Expose
private Boolean booleanValue;
@Expose
private @NonNull String stringValue;

/**
* Initialize this meta data value
* @param value the value assigned to this metadata value
*/
public MetaDataValue(@NonNull Object value) {
if (value instanceof Integer) {
intValue = (int)value;
} else if (value instanceof Float) {
floatValue = (float)value;
} else if (value instanceof Double) {
doubleValue = (double)value;
} else if (value instanceof Long) {
longValue = (long)value;
} else if (value instanceof Short) {
shortValue = (short)value;
} else if (value instanceof Byte) {
byteValue = (byte)value;
} else if (value instanceof Boolean) {
booleanValue = (boolean)value;
} else if (value instanceof String) {
stringValue = (String)value;
}
}

public int asInt() {
return intValue;
}

public float asFloat() {
return floatValue;
}

public double asDouble() {
return doubleValue;
}

public long asLong() {
return longValue;
}

public short asShort() {
return shortValue;
}

public byte asByte() {
return byteValue;
}

public boolean asBoolean() {
return booleanValue;
}

@NonNull
public String asString() {
return stringValue;
}
}
51 changes: 51 additions & 0 deletions src/main/java/world/bentobox/bentobox/api/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
Expand All @@ -29,6 +30,7 @@
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.events.OfflineMessageEvent;
import world.bentobox.bentobox.api.metadata.MetaDataValue;
import world.bentobox.bentobox.util.Util;

/**
Expand Down Expand Up @@ -631,4 +633,53 @@ public boolean equals(Object obj) {
public void setAddon(Addon addon) {
this.addon = addon;
}

/**
* Get all the meta data for this user
* @return the metaData
* @since 1.15.4
*/
@NonNull
public Map<String, MetaDataValue> getMetaData() {
return plugin.getPlayers().getPlayer(playerUUID).getMetaData();
}

/**
* Get meta data by key
* @param key - key
* @return optional value to which the specified key is mapped, or empty if there is no mapping for the key
* @since 1.15.4
*/
public Optional<MetaDataValue> getMetaData(String key) {
return Optional.ofNullable(plugin.getPlayers().getPlayer(playerUUID).getMetaData().get(key));
}

/**
* @param metaData the metaData to set
* @since 1.15.4
*/
public void setMetaData(Map<String, MetaDataValue> metaData) {
plugin.getPlayers().getPlayer(playerUUID).setMetaData(metaData);
}

/**
* Put a key, value string pair into the user's meta data
* @param key - key
* @param value - value
* @return the previous value associated with key, or empty if there was no mapping for key.
* @since 1.15.4
*/
public Optional<MetaDataValue> putMetaData(String key, MetaDataValue value) {
return Optional.ofNullable(plugin.getPlayers().getPlayer(playerUUID).getMetaData().put(key, value));
}

/**
* Remove meta data
* @param key - key to remove
* @return the previous value associated with key, or empty if there was no mapping for key.
* @since 1.15.4
*/
public Optional<MetaDataValue> removeMetaData(String key) {
return Optional.ofNullable(plugin.getPlayers().getPlayer(playerUUID).getMetaData().remove(key));
}
}
78 changes: 69 additions & 9 deletions src/main/java/world/bentobox/bentobox/database/objects/Island.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.logs.LogEntry;
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;
Expand All @@ -54,7 +56,7 @@
* @author Poslovitch
*/
@Table(name = "Islands")
public class Island implements DataObject {
public class Island implements DataObject, MetaDataAble {

// True if this island is deleted and pending deletion from the database
@Expose
Expand Down Expand Up @@ -171,6 +173,13 @@ public class Island implements DataObject {
@Nullable
private Boolean reserved = null;

/**
* A place to store meta data for this island.
* @since 1.15.4
*/
@Expose
private Map<String, MetaDataValue> metaData;

/*
* *************************** Constructors ******************************
*/
Expand Down Expand Up @@ -956,14 +965,14 @@ public boolean showInfo(User user) {
// Fixes #getLastPlayed() returning 0 when it is the owner's first connection.
long lastPlayed = (Bukkit.getServer().getOfflinePlayer(owner).getLastPlayed() != 0) ?
Bukkit.getServer().getOfflinePlayer(owner).getLastPlayed() : Bukkit.getServer().getOfflinePlayer(owner).getFirstPlayed();
user.sendMessage("commands.admin.info.last-login","[date]", new Date(lastPlayed).toString());

user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
String resets = String.valueOf(plugin.getPlayers().getResets(world, owner));
String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world));
user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total);
// Show team members
showMembers(user);
user.sendMessage("commands.admin.info.last-login","[date]", new Date(lastPlayed).toString());

user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
String resets = String.valueOf(plugin.getPlayers().getResets(world, owner));
String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world));
user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total);
// Show team members
showMembers(user);
}
Vector location = center.toVector();
user.sendMessage("commands.admin.info.island-location", "[xyz]", Util.xyz(location));
Expand Down Expand Up @@ -1251,4 +1260,55 @@ public String toString() {
+ ", levelHandicap=" + levelHandicap + ", spawnPoint=" + spawnPoint + ", doNotLoad=" + doNotLoad + "]";
}

/**
* @return the metaData
* @since 1.15.4
*/
@Override
public Map<String, MetaDataValue> getMetaData() {
return metaData;
}

/**
* Get meta data by key
* @param key - key
* @return the value to which the specified key is mapped, or null if there is no mapping for the key
* @since 1.15.4
*/
@Override
public MetaDataValue getMetaData(String key) {
return this.metaData.get(key);
}

/**
* @param metaData the metaData to set
* @since 1.15.4
*/
@Override
public void setMetaData(Map<String, MetaDataValue> metaData) {
this.metaData = metaData;
}

/**
* Put a key, value string pair into the island's meta data
* @param key - key
* @param value - value
* @return the previous value associated with key, or null if there was no mapping for key.
* @since 1.15.4
*/
@Override
public MetaDataValue putMetaData(String key, MetaDataValue value) {
return this.metaData.put(key, value);
}

/**
* Remove meta data
* @param key - key to remove
* @return the previous value associated with key, or null if there was no mapping for key.
* @since 1.15.4
*/
@Override
public MetaDataValue removeMetaData(String key) {
return this.metaData.remove(key);
}
}
Loading

2 comments on commit d7c7559

@BONNe
Copy link
Member

@BONNe BONNe commented on d7c7559 Dec 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I was waiting for something like that a while.
Now I can get rid of some tables.

@tastybento
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was working on Border and realized it was silly to create a database table for a single boolean.

Please sign in to comment.