Added object deletion to the database.

This is required when islands are reset or players deleted, for example.
The deletion is done based on the uniqueId of the object. Tested and
working for flat file and MySQL.

To make an island do /is create. To reset do /is reset.
This commit is contained in:
tastybento 2017-06-17 18:46:16 -07:00
parent 4609d86ff0
commit 3d74c4a427
9 changed files with 258 additions and 25 deletions

View File

@ -18,6 +18,7 @@ import us.tastybento.bskyblock.config.BSBLocale;
import us.tastybento.bskyblock.config.PluginConfig;
import us.tastybento.bskyblock.config.Settings;
import us.tastybento.bskyblock.database.BSBDatabase;
import us.tastybento.bskyblock.database.BSBDatabase.DatabaseType;
import us.tastybento.bskyblock.database.managers.IslandsManager;
import us.tastybento.bskyblock.database.managers.OfflineHistoryMessages;
import us.tastybento.bskyblock.database.managers.PlayersManager;
@ -64,7 +65,7 @@ public class BSkyBlock extends JavaPlugin{
Settings.dbName = "ASkyBlock";
Settings.dbUsername = "username";
Settings.dbPassword = "password";
*/
*/
playersManager = new PlayersManager(this);
islandsManager = new IslandsManager(this);
// Only load metrics if set to true in config

View File

@ -1,31 +1,60 @@
package us.tastybento.bskyblock.commands;
import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.util.VaultHelper;
public class AdminCommand extends BSBCommand{
BSkyBlock plugin;
public AdminCommand(BSkyBlock plugin) {
super(plugin, true);
// TODO Auto-generated constructor stub
}
@Override
public void setup() {
// TODO Auto-generated method stub
/* /asadmin delete <name> - delete name's island */
registerArgument(new String[] {"delete"}, new CommandArgumentHandler() {
@Override
public boolean canExecute(CommandSender sender, String label, String[] args) {
// TODO Auto-generated method stub
return true;
}
@Override
public void onExecute(CommandSender sender, String label, String[] args) {
}
@Override
public List<String> onTabComplete(CommandSender sender, String label, String[] args) {
// TODO Auto-generated method stub
return null;
}
@Override
public String[] getHelp(CommandSender sender, String label){
return new String[] {null, plugin.getLocale(sender).get("help.admin.delete")};
}
});
}
@Override
public boolean canExecute(CommandSender sender, String label) {
// TODO Auto-generated method stub
return false;
return true;
}
@Override
public void onExecuteDefault(CommandSender sender, String label, String[] args) {
// TODO Auto-generated method stub
}

View File

@ -255,11 +255,11 @@ public class IslandCommand extends BSBCommand{
}
Player player = (Player)sender;
if (plugin.getIslands().hasIsland(player.getUniqueId())) {
// Delete island
new DeleteIslandBlocks(plugin, plugin.getIslands().getIsland(player.getUniqueId()));
plugin.getIslands().deletePlayerIsland(player.getUniqueId(), true);
// Create new island
Schematic schematic = plugin.getSchematics().getSchematic("default");
plugin.getIslands().newIsland(player, schematic);
plugin.getIslands().newIsland(player, schematic);
} else {
Util.sendMessage(player, plugin.getLocale(player.getUniqueId()).get("error.noIsland"));
}

View File

@ -49,6 +49,11 @@ public class FlatFileDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
protected String createInsertQuery() {
return ""; // not used
}
@Override
protected String createDeleteQuery() {
// TODO Auto-generated method stub
return null;
}
/**
* Creates a <T> filled with values from the corresponding
* database file
@ -220,7 +225,7 @@ public class FlatFileDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
// Check if this field is the mandatory UniqueId field. This is used to identify this instantiation of the class
if (method.getName().equals("getUniqueId")) {
// If the object does not have a unique name assigned to it already, one is created at random
plugin.getLogger().info("DEBUG: uniqueId = " + value);
//plugin.getLogger().info("DEBUG: uniqueId = " + value);
String id = (String)value;
if (id.isEmpty()) {
id = databaseConnecter.getUniqueId(type.getSimpleName());
@ -230,7 +235,7 @@ public class FlatFileDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
// Save the name for when the file is saved
filename = id;
}
// UUID's need special serialization
// Collections need special serialization
if (propertyDescriptor.getPropertyType().equals(HashMap.class) || propertyDescriptor.getPropertyType().equals(Map.class)) {
// Maps need to have keys serialized
//plugin.getLogger().info("DEBUG: Map for " + field.getName());
@ -296,6 +301,10 @@ public class FlatFileDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
// If the value is null as a string, return null
return null;
}
// Bukkit may have deserialized the object already
if (clazz.equals(value.getClass())) {
return value;
}
// Types that need to be deserialized
if (clazz.equals(UUID.class)) {
value = UUID.fromString((String)value);
@ -325,4 +334,21 @@ public class FlatFileDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
return value;
}
@Override
protected void deleteObject(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException {
// The file name of the Yaml file.
PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", type);
Method method = propertyDescriptor.getReadMethod();
String fileName = (String) method.invoke(instance);
if (!fileName.endsWith(".yml")) {
fileName = fileName + ".yml";
}
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
File tableFolder = new File(dataFolder, type.getSimpleName());
if (tableFolder.exists()) {
File file = new File(tableFolder, fileName);
file.delete();
}
}
}

View File

@ -8,6 +8,7 @@ import java.util.List;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.database.DatabaseConnecter;
import us.tastybento.bskyblock.database.objects.Island;
/**
* An abstract class that handles insert/select-operations into/from a database
@ -36,6 +37,7 @@ public abstract class AbstractDatabaseHandler<T> {
/** The SQL-select- and insert query */
protected final String selectQuery;
protected final String insertQuery;
protected final String deleteQuery;
protected BSkyBlock plugin;
@ -57,15 +59,17 @@ public abstract class AbstractDatabaseHandler<T> {
this.type = type;
this.selectQuery = createSelectQuery();
this.insertQuery = createInsertQuery();
this.deleteQuery = createDeleteQuery();
}
/**
* Create the SQL-String to insert into / select from the database
* Create the SQL-String to insert into / select / delete from the database
* Not used in the flat file database
* @return the SQL-String
*/
protected abstract String createSelectQuery();
protected abstract String createInsertQuery();
protected abstract String createDeleteQuery();
/**
*
@ -142,4 +146,17 @@ public abstract class AbstractDatabaseHandler<T> {
*/
protected abstract void saveObject(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException, SQLException, SecurityException, InstantiationException, NoSuchMethodException;
/**
* Deletes the object with the unique id from the database
* @param instance
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws IntrospectionException
* @throws SQLException
* @throws SecurityException
* @throws NoSuchMethodException
*/
protected abstract void deleteObject(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException, SQLException, NoSuchMethodException, SecurityException;
}

View File

@ -1,5 +1,7 @@
package us.tastybento.bskyblock.database.managers;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
@ -30,6 +32,7 @@ import us.tastybento.bskyblock.database.objects.Island;
import us.tastybento.bskyblock.generators.IslandWorld;
import us.tastybento.bskyblock.schematics.Schematic;
import us.tastybento.bskyblock.schematics.Schematic.PasteReason;
import us.tastybento.bskyblock.util.DeleteIslandBlocks;
import us.tastybento.bskyblock.util.SafeSpotTeleport;
import us.tastybento.bskyblock.util.Util;
@ -175,6 +178,55 @@ public class IslandsManager {
}
}
/**
* Delete island owned by UniqueId
* @param uniqueId
*/
public void deleteIsland(UUID uniqueId){
if (islandsByUUID.containsKey(uniqueId)) {
Island island = islandsByLocation.get(uniqueId);
islandsByUUID.remove(uniqueId);
islandsByLocation.remove(island.getCenter());
}
}
/**
* Delete Island
* Called when an island is restarted or reset
*
* @param player
* - player name String
* @param removeBlocks
* - true to remove the island blocks
*/
public void deletePlayerIsland(final UUID player, boolean removeBlocks) {
// Removes the island
//getLogger().info("DEBUG: deleting player island");
//CoopPlay.getInstance().clearAllIslandCoops(player);
//getWarpSignsListener().removeWarp(player);
Island island = getIsland(player);
if (island != null) {
if (removeBlocks) {
removePlayersFromIsland(island, player);
new DeleteIslandBlocks(plugin, island);
try {
handler.deleteObject(island);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
island.setLocked(false);
island.setOwner(null);
}
//getServer().getPluginManager().callEvent(new IslandDeleteEvent(player, island.getCenter()));
} else {
plugin.getLogger().severe("Could not delete player: " + player.toString() + " island!");
//plugin.getServer().getPluginManager().callEvent(new IslandDeleteEvent(player, null));
}
//players.zeroPlayerData(player);
}
public Island getSpawn(){
return spawn;
}
@ -423,7 +475,7 @@ public class IslandsManager {
return true;
}
/**
* Determines a safe teleport spot on player's island or the team island
* they belong to.
@ -601,7 +653,7 @@ public class IslandsManager {
// Nothing worked
return null;
}
/**
* Checks if this location is safe for a player to teleport to. Used by
* warps and boat exits Unsafe is any liquid or air and also if there's no
@ -682,7 +734,7 @@ public class IslandsManager {
//Bukkit.getLogger().info("DEBUG: safe!");
return true;
}
/**
* Makes an island using schematic. No permission checks are made. They have to be decided
* before this method is called.
@ -690,15 +742,16 @@ public class IslandsManager {
* @param schematic
*/
public void newIsland(final Player player, final Schematic schematic) {
plugin.getLogger().info("DEBUG: new island");
//long time = System.nanoTime();
final UUID playerUUID = player.getUniqueId();
boolean firstTime = false;
if (!plugin.getPlayers().hasIsland(playerUUID)) {
firstTime = true;
}
//plugin.getLogger().info("DEBUG: finding island location");
plugin.getLogger().info("DEBUG: finding island location");
Location next = getNextIsland(player.getUniqueId());
//plugin.getLogger().info("DEBUG: found " + next);
plugin.getLogger().info("DEBUG: found " + next);
// Clear any old home locations (they should be clear, but just in case)
plugin.getPlayers().clearHomeLocations(playerUUID);
@ -829,17 +882,20 @@ public class IslandsManager {
* @return Location of island spot
*/
private Location getNextIsland(UUID playerUUID) {
plugin.getLogger().info("DEBUG: last = " + last);
// Find the next free spot
if (last == null) {
last = new Location(IslandWorld.getIslandWorld(), Settings.islandXOffset + Settings.islandStartX, Settings.islandHeight, Settings.islandZOffset + Settings.islandStartZ);
}
Location next = last.clone();
while (plugin.getIslands().isIsland(next)) {
plugin.getLogger().info("DEBUG: last 2 = " + last);
do {
plugin.getLogger().info("DEBUG: getting next loc");
next = nextGridLocation(next);
}
} while (isIsland(next));
// Make the last next, last
last = next.clone();
plugin.getLogger().info("DEBUG: last 3 = " + last);
return next;
}
@ -851,7 +907,7 @@ public class IslandsManager {
* @return Location of next free island
*/
private Location nextGridLocation(final Location lastIsland) {
// plugin.getLogger().info("DEBUG nextIslandLocation");
plugin.getLogger().info("DEBUG: nextIslandLocation - island distance = " + Settings.islandDistance);
final int x = lastIsland.getBlockX();
final int z = lastIsland.getBlockZ();
final Location nextPos = lastIsland;
@ -878,5 +934,39 @@ public class IslandsManager {
nextPos.setZ(nextPos.getZ() - Settings.islandDistance);
return nextPos;
}
/**
* This removes players from an island overworld and nether - used when reseting or deleting an island
* Mobs are killed when the chunks are refreshed.
* @param island to remove players from
* @param uuid
*/
public void removePlayersFromIsland(final Island island, UUID uuid) {
// Teleport players away
for (Player player : plugin.getServer().getOnlinePlayers()) {
if (island.inIslandSpace(player.getLocation().getBlockX(), player.getLocation().getBlockZ())) {
//plugin.getLogger().info("DEBUG: in island space");
// Teleport island players to their island home
if (!player.getUniqueId().equals(uuid) && (plugin.getPlayers().hasIsland(player.getUniqueId()) || plugin.getPlayers().inTeam(player.getUniqueId()))) {
//plugin.getLogger().info("DEBUG: home teleport");
homeTeleport(player);
} else {
//plugin.getLogger().info("DEBUG: move player to spawn");
// Move player to spawn
Island spawn = getSpawn();
if (spawn != null) {
// go to island spawn
player.teleport(IslandWorld.getIslandWorld().getSpawnLocation());
//plugin.getLogger().warning("During island deletion player " + player.getName() + " sent to spawn.");
} else {
if (!player.performCommand(Settings.SPAWNCOMMAND)) {
plugin.getLogger().warning(
"During island deletion player " + player.getName() + " could not be sent to spawn so was dropped, sorry.");
}
}
}
}
}
}
}

View File

@ -108,14 +108,14 @@ public class PlayersManager{
public Players addPlayer(final UUID playerUUID) {
if (playerUUID == null)
return null;
plugin.getLogger().info("DEBUG: added player " + playerUUID);
//plugin.getLogger().info("DEBUG: added player " + playerUUID);
if (!playerCache.containsKey(playerUUID)) {
plugin.getLogger().info("DEBUG: new player");
final Players player = new Players(playerUUID);
playerCache.put(playerUUID, player);
return player;
} else {
plugin.getLogger().info("DEBUG: returning cache");
//plugin.getLogger().info("DEBUG: known player");
return playerCache.get(playerUUID);
}
}

View File

@ -16,7 +16,6 @@ import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@ -293,6 +292,10 @@ public class MySQLDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
return sb.toString();
}
protected String createDeleteQuery() {
return "DELETE FROM [table_name] WHERE uniqueId = ?";
}
/**
* Inserts a <T> into the corresponding database-table
*
@ -721,4 +724,63 @@ public class MySQLDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
return value;
}
/* (non-Javadoc)
* @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#deleteObject(java.lang.Object)
*/
@Override
protected void deleteObject(T instance)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException, IntrospectionException, SQLException, NoSuchMethodException, SecurityException {
// Delete this object from all tables
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
// Try to connect to the database
connection = databaseConnecter.createConnection();
// Get the uniqueId. As each class extends DataObject, it must have this method in it.
Method getUniqueId = type.getMethod("getUniqueId");
String uniqueId = (String) getUniqueId.invoke(instance);
//plugin.getLogger().info("DEBUG: Unique Id = " + uniqueId);
if (uniqueId.isEmpty()) {
throw new SQLException("uniqueId is blank");
}
// Delete from the main table
// First substitution is the table name
// deleteQuery is created in super from the createInsertQuery() method
preparedStatement = connection.prepareStatement(deleteQuery.replace("[table_name]", "`" + type.getCanonicalName() + "`"));
// Second is the unique ID
preparedStatement.setString(1, uniqueId);
preparedStatement.addBatch();
plugin.getLogger().info("DEBUG: DELETE Query " + preparedStatement.toString());
preparedStatement.executeBatch();
// Delete from any sub tables created from the object
// Run through the fields in the class using introspection
for (Field field : type.getDeclaredFields()) {
// Get the field's property descriptor
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), type);
// Delete Collection tables
if (propertyDescriptor.getPropertyType().equals(Set.class) ||
propertyDescriptor.getPropertyType().equals(Map.class) ||
propertyDescriptor.getPropertyType().equals(HashMap.class) ||
propertyDescriptor.getPropertyType().equals(ArrayList.class)) {
// First substitution is the table name
preparedStatement = connection.prepareStatement(deleteQuery.replace("[table_name]", "`" + type.getCanonicalName() + "." + field.getName() + "`"));
// Second is the unique ID
preparedStatement.setString(1, uniqueId);
preparedStatement.addBatch();
// Execute
plugin.getLogger().info("DEBUG: " + preparedStatement.toString());
preparedStatement.executeBatch();
}
}
} finally {
// Close properly
MySQLDatabaseResourceCloser.close(preparedStatement);
}
}
}

View File

@ -47,12 +47,21 @@ public class DeleteIslandBlocks {
//private HashMap<Location, Material> blocksToClear = new HashMap<Location,Material>();
private NMSAbstraction nms = null;
/**
* Deletes the island
* @param plugin
* @param island
*/
public DeleteIslandBlocks(final BSkyBlock plugin, final Island island) {
plugin.getLogger().info("DEBUG: deleting the island");
final World world = island.getCenter().getWorld();
if (world == null)
return;
// Determine if blocks need to be cleaned up or not
boolean cleanUpBlocks = false;
plugin.getLogger().info("DEBUG: island protection = " + island.getProtectionRange());
// DEBUG
island.setProtectionRange(Settings.islandDistance);
if (Settings.islandDistance - island.getProtectionRange() < 16) {
cleanUpBlocks = true;
}
@ -61,8 +70,7 @@ public class DeleteIslandBlocks {
final int minz = island.getMinProtectedZ();
final int maxx = island.getMinProtectedX() + island.getProtectionRange();
final int maxz = island.getMinProtectedZ() + island.getProtectionRange();
// plugin.getLogger().info("DEBUG: protection limits are: " + minx +
// ", " + minz + " to " + maxx + ", " + maxz );
plugin.getLogger().info("DEBUG: protection limits are: " + minx + ", " + minz + " to " + maxx + ", " + maxz );
int islandSpacing = Settings.islandDistance - island.getProtectionRange();
int minxX = (island.getCenter().getBlockX() - range - islandSpacing);
int minzZ = (island.getCenter().getBlockZ() - range - islandSpacing);