Added experimental SQLite support.

This commit is contained in:
Fernando Pettinelli 2020-12-11 22:01:24 -03:00 committed by Brianna
parent 81f0d74120
commit 60b3d28794
10 changed files with 438 additions and 68 deletions

View File

@ -5,6 +5,9 @@ import com.songoda.core.SongodaPlugin;
import com.songoda.core.commands.CommandManager;
import com.songoda.core.compatibility.CompatibleMaterial;
import com.songoda.core.configuration.Config;
import com.songoda.core.database.DataMigrationManager;
import com.songoda.core.database.DatabaseConnector;
import com.songoda.core.database.SQLiteConnector;
import com.songoda.core.gui.GuiManager;
import com.songoda.core.hooks.EconomyManager;
import com.songoda.core.hooks.EntityStackerManager;
@ -17,6 +20,8 @@ import com.songoda.epicfarming.commands.CommandBoost;
import com.songoda.epicfarming.commands.CommandGiveFarmItem;
import com.songoda.epicfarming.commands.CommandReload;
import com.songoda.epicfarming.commands.CommandSettings;
import com.songoda.epicfarming.database.DataManager;
import com.songoda.epicfarming.database.migrations._1_InitialMigration;
import com.songoda.epicfarming.farming.Farm;
import com.songoda.epicfarming.farming.FarmManager;
import com.songoda.epicfarming.farming.FarmType;
@ -44,6 +49,7 @@ import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.permission.BasicPermission;
import org.apache.commons.lang.math.NumberUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
@ -78,7 +84,8 @@ public class EpicFarming extends SongodaPlugin {
private EntityUtils entityUtils;
private Storage storage;
private DatabaseConnector databaseConnector;
private DataManager dataManager;
public static EpicFarming getInstance() {
return INSTANCE;
@ -92,7 +99,8 @@ public class EpicFarming extends SongodaPlugin {
@Override
public void onPluginDisable() {
saveToFile();
this.storage.closeConnection();
for (Farm farm : farmManager.getFarms().values())
dataManager.updateItems(farm);
}
@Override
@ -125,9 +133,13 @@ public class EpicFarming extends SongodaPlugin {
new CommandSettings(this)
);
dataConfig.load();
this.databaseConnector = new SQLiteConnector(this);
this.getLogger().info("Data handler connected using SQLite.");
this.storage = new StorageYaml(this);
this.dataManager = new DataManager(this.databaseConnector, this);
DataMigrationManager dataMigrationManager = new DataMigrationManager(this.databaseConnector, this.dataManager,
new _1_InitialMigration());
dataMigrationManager.runMigrations();
this.loadLevelManager();
@ -162,62 +174,88 @@ public class EpicFarming extends SongodaPlugin {
}, 30L);
// Start auto save
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, this::saveToFile, 6000, 6000);
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
saveToFile();
for (Farm farm : farmManager.getFarms().values())
dataManager.updateItemsAsync(farm);
}, 6000, 6000);
}
@Override
public void onDataLoad() {
if (storage.containsGroup("farms")) {
for (StorageRow row : storage.getRowsByGroup("farms")) {
Location location = Methods.unserializeLocation(row.getKey());
if (location == null || location.getWorld() == null) continue;
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
// Legacy data! Yay!
File folder = getDataFolder();
File dataFile = new File(folder, "data.yml");
int level = 1;
int configLevel = row.get("level").asInt();
if (configLevel > 0) {
level = configLevel;
}
List<ItemStack> items = new ArrayList<ItemStack>();
List<ItemStack> configItems = row.get("contents").asItemStackList();
if (configItems != null && configItems.size() > 0) {
items = configItems;
}
UUID placedBY = null;
String configPlacedBy = row.get("placedby").asString();
if (configPlacedBy != null) {
placedBY = UUID.fromString(configPlacedBy);
boolean converted = false;
if (dataFile.exists()) {
converted = true;
Storage storage = new StorageYaml(this);
if (storage.containsGroup("farms")) {
console.sendMessage("[" + getDescription().getName() + "] " + ChatColor.RED +
"Conversion process starting. Do NOT turn off your server." +
"EpicFarming hasn't fully loaded yet, so make sure users don't" +
"interact with the plugin until the conversion process is complete.");
List<Farm> farms = new ArrayList<>();
for (StorageRow row : storage.getRowsByGroup("farms")) {
Location location = Methods.unserializeLocation(row.getKey());
if (location == null) continue;
if (row.get("level").asInt() == 0) continue;
String placedByStr = row.get("placedby").asString();
UUID placedBy = placedByStr == null ? null : UUID.fromString(placedByStr);
List<ItemStack> items = row.get("contents").asItemStackList();
if (items == null) {
items = new ArrayList<>();
}
FarmType farmType = FarmType.BOTH;
String farmTypeStr = row.get("farmtype").asString();
if (farmTypeStr != null) {
farmType = FarmType.valueOf(farmTypeStr);
}
Farm farm = new Farm(location, levelManager.getLevel(row.get("level").asInt()), placedBy);
farm.setFarmType(farmType);
farm.setItems(items);
farms.add(farm);
}
dataManager.createFarms(farms);
}
FarmType farmType = FarmType.BOTH;
String farmTypeStr = row.get("farmtype").asString();
if (farmTypeStr != null)
farmType = FarmType.valueOf(farmTypeStr);
// Adding in Boosts
if (storage.containsGroup("boosts")) {
for (StorageRow row : storage.getRowsByGroup("boosts")) {
if (row.get("uuid").asObject() == null)
continue;
Farm farm = new Farm(location, levelManager.getLevel(level), placedBY);
farm.setFarmType(farmType);
farm.setItems(items);
Bukkit.getScheduler().runTask(EpicFarming.getInstance(), () ->
farmManager.addFarm(location, farm));
dataManager.createBoost(new BoostData(
row.get("amount").asInt(),
Long.parseLong(row.getKey()),
UUID.fromString(row.get("uuid").asString())));
}
}
dataFile.delete();
}
}
// Adding in Boosts
if (storage.containsGroup("boosts")) {
for (StorageRow row : storage.getRowsByGroup("boosts")) {
final boolean finalConverted = converted;
dataManager.queueAsync(() -> {
if (finalConverted) {
console.sendMessage("[" + getDescription().getName() + "] " + ChatColor.GREEN + "Conversion complete :)");
}
BoostData boostData = new BoostData(
row.get("amount").asInt(),
Long.parseLong(row.getKey()),
UUID.fromString(row.get("player").asString()));
Bukkit.getScheduler().runTask(EpicFarming.getInstance(), () -> {
this.boostManager.addBoostToPlayer(boostData);
this.dataManager.getFarms((farms) -> {
this.farmManager.addFarms(farms.values());
this.dataManager.getBoosts((boosts) -> this.boostManager.addBoosts(boosts));
});
}
}
// Save data initially so that if the person reloads again fast they don't lose all their data.
this.saveToFile();
}, "create");
});
}
@Override
@ -289,8 +327,6 @@ public class EpicFarming extends SongodaPlugin {
for (Module module : level.getRegisteredModules())
module.saveDataToFile();
}
storage.doSave();
}
public int getLevelFromItem(ItemStack item) {
@ -351,4 +387,12 @@ public class EpicFarming extends SongodaPlugin {
public EntityUtils getEntityUtils() {
return entityUtils;
}
public DatabaseConnector getDatabaseConnector() {
return databaseConnector;
}
public DataManager getDataManager() {
return dataManager;
}
}

View File

@ -1,9 +1,8 @@
package com.songoda.epicfarming.boost;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import com.songoda.epicfarming.EpicFarming;
import java.util.*;
public class BoostManager {
@ -17,6 +16,11 @@ public class BoostManager {
this.registeredBoosts.remove(data);
}
public void addBoosts(Collection<BoostData> boosts) {
for (BoostData boost : boosts)
this.registeredBoosts.add(boost);
}
public Set<BoostData> getBoosts() {
return Collections.unmodifiableSet(registeredBoosts);
}
@ -26,6 +30,7 @@ public class BoostManager {
for (BoostData boostData : registeredBoosts) {
if (boostData.getPlayer().toString().equals(player.toString())) {
if (System.currentTimeMillis() >= boostData.getEndTime()) {
EpicFarming.getInstance().getDataManager().deleteBoost(boostData);
removeBoostFromPlayer(boostData);
}
return boostData;

View File

@ -50,6 +50,7 @@ public class CommandBoost extends AbstractCommand {
}
BoostData boostData = new BoostData(Integer.parseInt(args[1]), duration == 0L ? Long.MAX_VALUE : System.currentTimeMillis() + duration, player.getUniqueId());
plugin.getDataManager().createBoost(boostData);
plugin.getBoostManager().addBoostToPlayer(boostData);
plugin.getLocale().newMessage("&7Successfully boosted &6" + Bukkit.getPlayer(args[0]).getName()
+ "'s &7farms by &6" + args[1] + "x" + (duration == 0L ? "" : (" for " + Methods.makeReadable(duration))) + "&7.").sendPrefixedMessage(sender);

View File

@ -0,0 +1,240 @@
package com.songoda.epicfarming.database;
import com.songoda.core.database.DataManagerAbstract;
import com.songoda.core.database.DatabaseConnector;
import com.songoda.core.nms.NmsManager;
import com.songoda.core.nms.nbt.NBTCore;
import com.songoda.core.nms.nbt.NBTItem;
import com.songoda.epicfarming.EpicFarming;
import com.songoda.epicfarming.boost.BoostData;
import com.songoda.epicfarming.farming.Farm;
import com.songoda.epicfarming.farming.FarmType;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;
import java.util.function.Consumer;
public class DataManager extends DataManagerAbstract {
public DataManager(DatabaseConnector connector, Plugin plugin) {
super(connector, plugin);
}
public void createBoost(BoostData boostData) {
this.async(() -> this.databaseConnector.connect(connection -> {
String createBoostedPlayer = "INSERT INTO " + this.getTablePrefix() + "boosted_players (player, multiplier, end_time) VALUES (?, ?, ?)";
try (PreparedStatement statement = connection.prepareStatement(createBoostedPlayer)) {
statement.setString(1, boostData.getPlayer().toString());
statement.setInt(2, boostData.getMultiplier());
statement.setLong(3, boostData.getEndTime());
statement.executeUpdate();
}
}));
}
public void getBoosts(Consumer<List<BoostData>> callback) {
List<BoostData> boosts = new ArrayList<>();
this.async(() -> this.databaseConnector.connect(connection -> {
try (Statement statement = connection.createStatement()) {
String selectBoostedPlayers = "SELECT * FROM " + this.getTablePrefix() + "boosted_players";
ResultSet result = statement.executeQuery(selectBoostedPlayers);
while (result.next()) {
UUID player = UUID.fromString(result.getString("player"));
int multiplier = result.getInt("multiplier");
long endTime = result.getLong("end_time");
boosts.add(new BoostData(multiplier, endTime, player));
}
}
this.sync(() -> callback.accept(boosts));
}));
}
public void deleteBoost(BoostData boostData) {
this.async(() -> this.databaseConnector.connect(connection -> {
String deleteBoost = "DELETE FROM " + this.getTablePrefix() + "boosted_players WHERE end_time = ?";
try (PreparedStatement statement = connection.prepareStatement(deleteBoost)) {
statement.setLong(1, boostData.getEndTime());
statement.executeUpdate();
}
}));
}
public void createFarm(Farm farm) {
this.queueAsync(() -> this.databaseConnector.connect(connection -> {
String createFarm = "INSERT INTO " + this.getTablePrefix() + "active_farms (farm_type, level, placed_by, world, x, y, z) VALUES (?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement statement = connection.prepareStatement(createFarm)) {
statement.setString(1, farm.getFarmType().name());
statement.setInt(2, farm.getLevel().getLevel());
statement.setString(3, farm.getPlacedBy().toString());
statement.setString(4, farm.getLocation().getWorld().getName());
statement.setInt(5, farm.getLocation().getBlockX());
statement.setInt(6, farm.getLocation().getBlockY());
statement.setInt(7, farm.getLocation().getBlockZ());
statement.executeUpdate();
}
int farmId = this.lastInsertedId(connection, "active_farms");
farm.setId(farmId);
String createItem = "INSERT INTO " + this.getTablePrefix() + "items (farm_id, item) VALUES (?, ?)";
try (PreparedStatement statement = connection.prepareStatement(createItem)) {
for (ItemStack item : farm.getItems()) {
statement.setInt(1, farm.getId());
try (ByteArrayOutputStream stream = new ByteArrayOutputStream(); BukkitObjectOutputStream bukkitStream = new BukkitObjectOutputStream(stream)) {
bukkitStream.writeObject(item);
statement.setString(2, Base64.getEncoder().encodeToString(stream.toByteArray()));
} catch (IOException e) {
e.printStackTrace();
continue;
}
statement.addBatch();
}
statement.executeBatch();
}
}), "create");
}
public void createFarms(List<Farm> farms) {
for (Farm farm : farms) {
createFarm(farm);
}
}
public void updateFarm(Farm farm) {
this.async(() -> this.databaseConnector.connect(connection -> {
String updateFarm = "UPDATE " + this.getTablePrefix() + "active_farms SET level = ?, farm_type = ? WHERE id = ?";
try (PreparedStatement statement = connection.prepareStatement(updateFarm)) {
statement.setInt(1, farm.getLevel().getLevel());
statement.setString(2, farm.getFarmType().name());
statement.setInt(3, farm.getId());
statement.executeUpdate();
}
}));
}
public void deleteFarm(Farm farm) {
this.async(() -> this.databaseConnector.connect(connection -> {
String deleteFarm = "DELETE FROM " + this.getTablePrefix() + "active_farms WHERE id = ?";
try (PreparedStatement statement = connection.prepareStatement(deleteFarm)) {
statement.setInt(1, farm.getId());
statement.executeUpdate();
}
String deleteItems = "DELETE FROM " + this.getTablePrefix() + "items WHERE farm_id = ?";
try (PreparedStatement statement = connection.prepareStatement(deleteItems)) {
statement.setInt(1, farm.getId());
statement.executeUpdate();
}
}));
}
public void updateItemsAsync(Farm farm) {
this.async(() -> updateItems(farm));
}
public void updateItems(Farm farm) {
this.databaseConnector.connect(connection -> {
String deleteItems = "DELETE FROM " + this.getTablePrefix() + "items WHERE farm_id = ?";
try (PreparedStatement statement = connection.prepareStatement(deleteItems)) {
statement.setInt(1, farm.getId());
statement.executeUpdate();
}
String createItem = "INSERT INTO " + this.getTablePrefix() + "items (farm_id, item) VALUES (?, ?)";
try (PreparedStatement statement = connection.prepareStatement(createItem)) {
for (ItemStack item : farm.getItems()) {
statement.setInt(1, farm.getId());
try (ByteArrayOutputStream stream = new ByteArrayOutputStream(); BukkitObjectOutputStream bukkitStream = new BukkitObjectOutputStream(stream)) {
bukkitStream.writeObject(item);
statement.setString(2, Base64.getEncoder().encodeToString(stream.toByteArray()));
} catch (IOException e) {
e.printStackTrace();
continue;
}
statement.addBatch();
}
statement.executeBatch();
}
});
}
public void getFarms(Consumer<Map<Integer, Farm>> callback) {
this.async(() -> this.databaseConnector.connect(connection -> {
Map<Integer, Farm> farms = new HashMap<>();
try (Statement statement = connection.createStatement()) {
String selectFarms = "SELECT * FROM " + this.getTablePrefix() + "active_farms";
ResultSet result = statement.executeQuery(selectFarms);
while (result.next()) {
World world = Bukkit.getWorld(result.getString("world"));
if (world == null) {
continue;
}
int id = result.getInt("id");
int level = result.getInt("level");
String placedByStr = result.getString("placed_by");
UUID placedBy = placedByStr == null ? null : UUID.fromString(result.getString("placed_by"));
FarmType farmType = FarmType.valueOf(result.getString("farm_type"));
int x = result.getInt("x");
int y = result.getInt("y");
int z = result.getInt("z");
Location location = new Location(world, x, y, z);
Farm farm = new Farm(location, EpicFarming.getInstance().getLevelManager().getLevel(level), placedBy);
farm.setId(id);
farm.setFarmType(farmType);
farms.put(id, farm);
}
}
try (Statement statement = connection.createStatement()) {
String selectItems = "SELECT * FROM " + this.getTablePrefix() + "items";
ResultSet result = statement.executeQuery(selectItems);
while (result.next()) {
int id = result.getInt("farm_id");
ItemStack item = null;
try (BukkitObjectInputStream stream = new BukkitObjectInputStream(
new ByteArrayInputStream(Base64.getDecoder().decode(result.getString("item"))))) {
item = (ItemStack) stream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
Farm farm = farms.get(id);
if (farm == null) {
break;
}
if (item != null) {
farm.addItem(item);
}
}
}
this.sync(() -> callback.accept(farms));
}));
}
}

View File

@ -0,0 +1,54 @@
package com.songoda.epicfarming.database.migrations;
import com.songoda.core.database.DataMigration;
import com.songoda.core.database.MySQLConnector;
import com.songoda.epicfarming.EpicFarming;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class _1_InitialMigration extends DataMigration {
public _1_InitialMigration() {
super(1);
}
@Override
public void migrate(Connection connection, String tablePrefix) throws SQLException {
String autoIncrement = EpicFarming.getInstance().getDatabaseConnector() instanceof MySQLConnector ? " AUTO_INCREMENT" : "";
// Create farms table.
try (Statement statement = connection.createStatement()) {
statement.execute("CREATE TABLE " + tablePrefix + "active_farms (" +
"id INTEGER PRIMARY KEY" + autoIncrement + ", " +
"farm_type TEXT NOT NULL, " +
"level INTEGER NOT NULL, " +
"placed_by VARCHAR(36), " +
"world TEXT NOT NULL, " +
"x DOUBLE NOT NULL, " +
"y DOUBLE NOT NULL, " +
"z DOUBLE NOT NULL " +
")");
}
// Create items.
// Items are stored as Base64. Dunno if this is the most efficient way to
// store them, though.
try (Statement statement = connection.createStatement()) {
statement.execute("CREATE TABLE " + tablePrefix + "items (" +
"farm_id INTEGER NOT NULL, " +
"item TEXT NOT NULL" +
")");
}
// Create player boosts
try (Statement statement = connection.createStatement()) {
statement.execute("CREATE TABLE " + tablePrefix + "boosted_players (" +
"player VARCHAR(36) NOT NULL, " +
"multiplier INTEGER NOT NULL," +
"end_time BIGINT NOT NULL " +
")");
}
}
}

View File

@ -31,6 +31,9 @@ public class Farm {
// It is reset on every plugin load.
private UUID uniqueId = UUID.randomUUID();
// Id for database usage.
private int id;
private static final Random random = new Random();
private final List<Block> cachedCrops = new ArrayList<>();
private final List<ItemStack> items = new ArrayList<>();
@ -100,6 +103,7 @@ public class Farm {
private void upgradeFinal(Level level, Player player) {
EpicFarming instance = EpicFarming.getInstance();
this.level = level;
instance.getDataManager().updateFarm(this);
if (instance.getLevelManager().getHighestLevel() != level) {
instance.getLocale().getMessage("event.upgrade.success")
.processPlaceholder("level", level.getLevel()).sendPrefixedMessage(player);
@ -311,9 +315,19 @@ public class Farm {
farmType = FarmType.CROPS;
break;
}
EpicFarming.getInstance().getDataManager().updateFarm(this);
}
public void setFarmType(FarmType farmType) {
this.farmType = farmType;
EpicFarming.getInstance().getDataManager().updateFarm(this);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}

View File

@ -8,6 +8,7 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -26,6 +27,11 @@ public class FarmManager {
registeredFarms.put(roundLocation(location), farm);
}
public void addFarms(Collection<Farm> farms) {
for (Farm farm : farms)
registeredFarms.put(farm.getLocation(), farm);
}
public Farm removeFarm(Location location) {
return registeredFarms.remove(roundLocation(location));
}

View File

@ -50,21 +50,21 @@ public class OverviewGui extends Gui {
this.setDefaultItem(null);
GuiUtils.mirrorFill(this, 0, 0, false, true, glass2);
GuiUtils.mirrorFill(this, 0, 1, false, true, glass2);
GuiUtils.mirrorFill(this, 0, 2, false, true, glass3);
GuiUtils.mirrorFill(this, 1, 0, false, true, glass2);
GuiUtils.mirrorFill(this, 1, 1, false, true, glass3);
GuiUtils.mirrorFill(this, 2, 0, false, true, glass2);
GuiUtils.mirrorFill(this, 2, 1, false, true, glass2);
GuiUtils.mirrorFill(this, 2, 2, false, true, glass3);
mirrorFill(0, 0, false, true, glass2);
mirrorFill(0, 1, false, true, glass2);
mirrorFill(0, 2, false, true, glass3);
mirrorFill(1, 0, false, true, glass2);
mirrorFill(1, 1, false, true, glass3);
mirrorFill(2, 0, false, true, glass2);
mirrorFill(2, 1, false, true, glass2);
mirrorFill(2, 2, false, true, glass3);
GuiUtils.mirrorFill(this, 0, 3, false, true, glass1);
GuiUtils.mirrorFill(this, 0, 4, false, false, glass1);
GuiUtils.mirrorFill(this, 1, 3, false, true, glass1);
GuiUtils.mirrorFill(this, 1, 2, false, true, glass1);
GuiUtils.mirrorFill(this, 2, 3, false, true, glass1);
GuiUtils.mirrorFill(this, 2, 4, false, false, glass1);
mirrorFill(0, 3, false, true, glass1);
mirrorFill(0, 4, false, false, glass1);
mirrorFill(1, 3, false, true, glass1);
mirrorFill(1, 2, false, true, glass1);
mirrorFill(2, 3, false, true, glass1);
mirrorFill(2, 4, false, false, glass1);
// enable page events
if (level.getPages() > 1) {

View File

@ -96,6 +96,7 @@ public class BlockListeners implements Listener {
Farm farm = new Farm(location, plugin.getLevelManager().getLevel(level == 0 ? 1 : level), e.getPlayer().getUniqueId());
plugin.getFarmManager().addFarm(location, farm);
plugin.getDataManager().createFarm(farm);
farm.tillLand();
}, 1);
@ -110,6 +111,8 @@ public class BlockListeners implements Listener {
if (farm == null) return;
plugin.getDataManager().deleteFarm(farm);
FarmTask.getCrops(farm, false);
event.setCancelled(true);
@ -136,6 +139,8 @@ public class BlockListeners implements Listener {
if (farm == null) return;
FarmTask.getCrops(farm, false);
plugin.getDataManager().deleteFarm(farm);
event.setCancelled(true);
ItemStack item = plugin.makeFarmItem(farm.getLevel());

View File

@ -109,6 +109,7 @@ public class EntityListeners implements Listener {
event.blockList().remove(block);
Farm farm = plugin.getFarmManager().removeFarm(block.getLocation());
plugin.getDataManager().deleteFarm(farm);
FarmTask.getCrops(farm, false);