Minepacks/Minepacks/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Database.java

297 lines
9.4 KiB
Java

/*
* Copyright (C) 2023 GeorgH93
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.pcgamingfreaks.Minepacks.Bukkit.Database;
import at.pcgamingfreaks.ConsoleColor;
import at.pcgamingfreaks.Database.ConnectionProvider.ConnectionProvider;
import at.pcgamingfreaks.Minepacks.Bukkit.API.Callback;
import at.pcgamingfreaks.Minepacks.Bukkit.Backpack;
import at.pcgamingfreaks.Minepacks.Bukkit.Database.UnCacheStrategies.OnDisconnect;
import at.pcgamingfreaks.Minepacks.Bukkit.Database.UnCacheStrategies.UnCacheStrategy;
import at.pcgamingfreaks.Minepacks.Bukkit.Minepacks;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class Database implements Listener
{
protected static final String START_UUID_UPDATE = "Start updating database to UUIDs ...", UUIDS_UPDATED = "Updated %d accounts to UUIDs.";
public static final String MESSAGE_UNKNOWN_DB_TYPE = ConsoleColor.RED + "Unknown database type \"%s\"!" + ConsoleColor.RESET;
protected final Minepacks plugin;
protected final InventorySerializer itsSerializer;
protected final boolean onlineUUIDs, bungeeCordMode, forceSaveOnUnload;
protected boolean useUUIDSeparators, asyncSave = true;
protected long maxAge;
private final Map<OfflinePlayer, Backpack> backpacks = new ConcurrentHashMap<>();
private final UnCacheStrategy unCacheStrategie;
private final File backupFolder;
public Database(Minepacks mp)
{
plugin = mp;
itsSerializer = new InventorySerializer(plugin.getLogger());
useUUIDSeparators = plugin.getConfiguration().getUseUUIDSeparators();
onlineUUIDs = plugin.getConfiguration().useOnlineUUIDs();
bungeeCordMode = plugin.getConfiguration().isBungeeCordModeEnabled();
forceSaveOnUnload = plugin.getConfiguration().isForceSaveOnUnloadEnabled();
maxAge = plugin.getConfiguration().getAutoCleanupMaxInactiveDays();
unCacheStrategie = bungeeCordMode ? new OnDisconnect(this) : UnCacheStrategy.getUnCacheStrategie(this);
backupFolder = new File(this.plugin.getDataFolder(), "backups");
if(!backupFolder.exists() && !backupFolder.mkdirs()) mp.getLogger().info("Failed to create backups folder.");
}
public void init()
{
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
public void close()
{
HandlerList.unregisterAll(this);
asyncSave = false;
backpacks.forEach((key, value) -> value.closeAll());
backpacks.clear();
unCacheStrategie.close();
}
public static @Nullable Database getDatabase(Minepacks plugin)
{
try
{
String dbType = plugin.getConfiguration().getDatabaseType();
ConnectionProvider connectionProvider = null;
if(dbType.equals("shared") || dbType.equals("external") || dbType.equals("global"))
{
/*if[STANDALONE]
plugin.getLogger().warning(ConsoleColor.RED + "The shared database connection option is not available in standalone mode!" + ConsoleColor.RESET);
return null;
else[STANDALONE]*/
at.pcgamingfreaks.PluginLib.Database.DatabaseConnectionPool pool = at.pcgamingfreaks.PluginLib.Bukkit.PluginLib.getInstance().getDatabaseConnectionPool();
if(pool == null)
{
plugin.getLogger().warning(ConsoleColor.RED + "The shared connection pool is not initialized correctly!" + ConsoleColor.RESET);
return null;
}
dbType = pool.getDatabaseType().toLowerCase(Locale.ROOT);
connectionProvider = pool.getConnectionProvider();
/*end[STANDALONE]*/
}
Database database;
switch(dbType)
{
case "mysql": database = new MySQL(plugin, connectionProvider); break;
case "sqlite": database = new SQLite(plugin, connectionProvider); break;
case "flat":
case "file":
case "files":
database = new Files(plugin); break;
default: plugin.getLogger().warning(String.format(MESSAGE_UNKNOWN_DB_TYPE, plugin.getConfiguration().getDatabaseType())); return null;
}
database.init();
return database;
}
catch(IllegalStateException ignored) {}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
public void backup(@NotNull Backpack backpack)
{
writeBackup(backpack.getOwner().getName(), getPlayerFormattedUUID(backpack.getOwner()), itsSerializer.getUsedSerializer(), itsSerializer.serialize(backpack.getInventory()));
}
protected void writeBackup(@Nullable String userName, @NotNull String userIdentifier, final int usedSerializer, final @NotNull byte[] data)
{
if(userIdentifier.equalsIgnoreCase(userName)) userName = null;
if(userName != null) userIdentifier = userName + "_" + userIdentifier;
final File save = new File(backupFolder, userIdentifier + "_" + System.currentTimeMillis() + Files.EXT);
try(FileOutputStream fos = new FileOutputStream(save))
{
fos.write(usedSerializer);
fos.write(data);
plugin.getLogger().info("Backup of the backpack has been created: " + save.getAbsolutePath());
}
catch(Exception e)
{
plugin.getLogger().warning(ConsoleColor.RED + "Failed to write backup! Error: " + e.getMessage() + ConsoleColor.RESET);
}
}
public @Nullable ItemStack[] loadBackup(final String backupName)
{
File backup = new File(backupFolder, backupName + Files.EXT);
return Files.readFile(itsSerializer, backup, plugin.getLogger());
}
public ArrayList<String> getBackups()
{
File[] files = backupFolder.listFiles((dir, name) -> name.endsWith(Files.EXT));
if(files != null)
{
ArrayList<String> backups = new ArrayList<>(files.length);
for(File file : files)
{
if(!file.isFile()) continue;
backups.add(file.getName().replaceAll(Files.EXT_REGEX, ""));
}
return backups;
}
return new ArrayList<>();
}
protected String getPlayerFormattedUUID(OfflinePlayer player)
{
return (useUUIDSeparators) ? player.getUniqueId().toString() : player.getUniqueId().toString().replace("-", "");
}
public @NotNull Collection<Backpack> getLoadedBackpacks()
{
return backpacks.values();
}
/**
* Gets a backpack for a player. This only includes backpacks that are cached! Do not use it unless you are sure that you only want to use cached data!
*
* @param player The player who's backpack should be retrieved.
* @return The backpack for the player. null if the backpack is not in the cache.
*/
public @Nullable Backpack getBackpack(@Nullable OfflinePlayer player)
{
return (player == null) ? null : backpacks.get(player);
}
public void getBackpack(final OfflinePlayer player, final Callback<at.pcgamingfreaks.Minepacks.Bukkit.API.Backpack> callback, final boolean createNewOnFail)
{
if(player == null || player.getClass().getName().contains("NPC"))
{
return;
}
Backpack lbp = backpacks.get(player);
if(lbp == null)
{
loadBackpack(player, new Callback<Backpack>()
{
@Override
public void onResult(Backpack backpack)
{
backpacks.put(player, backpack);
callback.onResult(backpack);
}
@Override
public void onFail()
{
if(createNewOnFail)
{
Backpack backpack = new Backpack(player);
backpacks.put(player, backpack);
callback.onResult(backpack);
}
else
{
callback.onFail();
}
}
});
}
else
{
callback.onResult(lbp);
}
}
public void getBackpack(final OfflinePlayer player, final Callback<at.pcgamingfreaks.Minepacks.Bukkit.API.Backpack> callback)
{
getBackpack(player, callback, true);
}
public void unloadBackpack(Backpack backpack)
{
if (forceSaveOnUnload)
{
backpack.forceSave();
}
else
{
backpack.save();
}
backpacks.remove(backpack.getOwner());
}
public void asyncLoadBackpack(final OfflinePlayer player)
{
if(player != null && backpacks.get(player) == null)
{
loadBackpack(player, new Callback<Backpack>()
{
@Override
public void onResult(Backpack backpack)
{
backpacks.put(player, backpack);
}
@Override
public void onFail()
{
backpacks.put(player, new Backpack(player));
}
});
}
}
@EventHandler
public void onPlayerLoginEvent(PlayerJoinEvent event)
{
updatePlayerAndLoadBackpack(event.getPlayer());
}
// DB Functions
public void updatePlayerAndLoadBackpack(Player player)
{
updatePlayer(player);
if(!bungeeCordMode) asyncLoadBackpack(player);
}
public abstract void updatePlayer(Player player);
public abstract void saveBackpack(Backpack backpack);
public void syncCooldown(Player player, long time) {}
public void getCooldown(final Player player, final Callback<Long> callback) {}
protected abstract void loadBackpack(final OfflinePlayer player, final Callback<Backpack> callback);
}