MC 1.13 upgrade support

This commit adds the "/backpack rewrite" command which enables admins to convert the backpacks into an upgradable format
All backpacks written with this version can be upgraded to MC 1.13 or newer!
This commit is contained in:
GeorgH93 2018-12-04 18:36:26 +01:00
parent b060a64a43
commit 2f8e062216
No known key found for this signature in database
GPG Key ID: D1630D37F9E4B3C8
7 changed files with 185 additions and 27 deletions

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>at.pcgamingfreaks</groupId>
<artifactId>MinePacks</artifactId>
<version>1.17.14-SNAPSHOT</version>
<version>1.18-SNAPSHOT</version>
<scm>
<connection>scm:git:git@github.com:GeorgH93/Minepacks.git</connection>
@ -58,7 +58,7 @@
<dependency>
<groupId>at.pcgamingfreaks</groupId>
<artifactId>PluginLib</artifactId>
<version>1.0.5-SNAPSHOT</version>
<version>1.0.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>

View File

@ -93,4 +93,7 @@ permissions:
default: op
backpack.ignoreGameMode:
description: Allows to bypass the game-mode restriction.
default: op
backpack.rewrite:
description: Converts all stored backpacks to the newest format.
default: op

View File

@ -26,7 +26,7 @@
import java.util.HashMap;
import java.util.Map;
public class Database
public abstract class Database
{
protected final MinePacks plugin;
protected final InventorySerializer itsSerializer;
@ -166,14 +166,19 @@ public void onFail()
}
}
// DB Functions
public void close()
public void closeAllBackpacks()
{
// Ensure that all backpacks are closed and saved before killing the database
for(Map.Entry<OfflinePlayer, Backpack> backpackEntry : backpacks.entrySet())
{
backpackEntry.getValue().closeAll();
}
}
// DB Functions
public void close()
{
closeAllBackpacks();
backpacks.clear();
}
@ -208,4 +213,6 @@ public interface Callback<T>
void onFail();
}
public abstract void rewrite();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2015 GeorgH93
* Copyright (C) 2014-2018 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
@ -146,7 +146,38 @@ public Backpack loadBackpack(OfflinePlayer player)
}
return null;
}
@Override
public void rewrite()
{
File[] allFiles = saveFolder.listFiles(new BackpackFileFilter());
if(allFiles == null) return;
for (File file : allFiles)
{
try
{
FileInputStream fis = new FileInputStream(file);
int v = fis.read();
byte[] backpackData = new byte[(int) (file.length() - 1)];
//noinspection ResultOfMethodCallIgnored
fis.read(backpackData);
fis.close();
if(v != itsSerializer.getUsedSerializer())
{
FileOutputStream fos = new FileOutputStream(file);
fos.write(itsSerializer.getUsedSerializer());
fos.write(itsSerializer.serialize(itsSerializer.deserialize(backpackData, v)));
fos.flush();
fos.close();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
class BackpackFileFilter extends FileFilter implements FilenameFilter
{
String description, extension;
@ -169,10 +200,7 @@ public boolean accept(File file)
if (!file.isDirectory())
{
String path = file.getAbsolutePath().toLowerCase();
if ((path.endsWith(extension) && (path.charAt(path.length() - extension.length() - 1)) == '.'))
{
return true;
}
return (path.endsWith(extension) && (path.charAt(path.length() - extension.length() - 1)) == '.');
}
return false;
}

View File

@ -20,6 +20,7 @@
import at.pcgamingfreaks.Bukkit.ItemStackSerializer.BukkitItemStackSerializer;
import at.pcgamingfreaks.Bukkit.ItemStackSerializer.ItemStackSerializer;
import at.pcgamingfreaks.Bukkit.ItemStackSerializer.NBTItemStackSerializer;
import at.pcgamingfreaks.Bukkit.ItemStackSerializer.NBTItemStackSerializerGen2;
import at.pcgamingfreaks.Bukkit.MCVersion;
import at.pcgamingfreaks.ConsoleColor;
@ -31,8 +32,8 @@
public class InventorySerializer
{
private ItemStackSerializer serializer, baseItemStackSerializer = new BukkitItemStackSerializer();
private int usedSerializer = 1;
private ItemStackSerializer serializer, serializerGen1, serializerGen2, bukkitItemStackSerializer = new BukkitItemStackSerializer();
private int usedSerializer = 2;
private Logger logger;
public InventorySerializer(Logger logger)
@ -45,9 +46,20 @@ public InventorySerializer(Logger logger)
{
serializer = new CauldronNBTItemStackSerializer();
}
else if(NBTItemStackSerializer.isMCVersionCompatible())
else
{
serializer = new NBTItemStackSerializer();
if(NBTItemStackSerializer.isMCVersionCompatible())
{
serializerGen1 = new NBTItemStackSerializer(logger);
usedSerializer = 1;
serializer = serializerGen1;
}
if(NBTItemStackSerializerGen2.isMCVersionCompatible())
{
serializerGen2 = new NBTItemStackSerializerGen2(logger);
usedSerializer = 2;
serializer = serializerGen2;
}
}
}
catch(Exception e)
@ -57,7 +69,7 @@ else if(NBTItemStackSerializer.isMCVersionCompatible())
if(serializer == null)
{
usedSerializer = 0;
serializer = baseItemStackSerializer;
serializer = bukkitItemStackSerializer;
}
}
@ -66,19 +78,21 @@ public byte[] serialize(Inventory inv)
return serializer.serialize(inv.getContents());
}
public byte[] serialize(ItemStack[] inv)
{
return serializer.serialize(inv);
}
public ItemStack[] deserialize(byte[] data, int usedSerializer)
{
switch(usedSerializer)
{
case 0: return baseItemStackSerializer.deserialize(data);
case 1:
if(usedSerializer != this.usedSerializer)
{
logger.warning(ConsoleColor.RED + "No compatible serializer for item format available!" + ConsoleColor.RESET);
return null;
}
default: return serializer.deserialize(data);
case 0: return bukkitItemStackSerializer.deserialize(data);
case 1: return serializerGen1.deserialize(data);
case 2: return serializerGen2.deserialize(data);
default: logger.warning(ConsoleColor.RED + "No compatible serializer for item format available!" + ConsoleColor.RESET);
}
return null;
}
public int getUsedSerializer()

View File

@ -39,7 +39,7 @@ public abstract class SQL extends Database
protected String tablePlayers, tableBackpacks; // Table Names
protected String fieldName, fieldPlayerID, fieldUUID, fieldBPOwner, fieldBPITS, fieldBPVersion, fieldBPLastUpdate; // Table Fields
protected String queryUpdatePlayerAdd, queryGetPlayerID, queryInsertBP, queryUpdateBP, queryGetBP, queryDeleteOldBackpacks, queryGetUnsetOrInvalidUUIDs, queryFixUUIDs; // DB Querys
protected String queryUpdatePlayerAdd, queryGetPlayerID, queryInsertBP, queryUpdateBP, queryGetBP, queryDeleteOldBackpacks, queryGetUnsetOrInvalidUUIDs, queryFixUUIDs, queryGetBPs, queryRewriteBPs; // DB Querys
protected boolean updatePlayer;
public SQL(MinePacks mp)
@ -207,6 +207,8 @@ protected final void buildQuerys()
queryGetUnsetOrInvalidUUIDs = "SELECT `{FieldPlayerID}`,`{FieldName}`,`{FieldUUID}` FROM `{TablePlayers}` WHERE `{FieldUUID}` IS NULL OR `{FieldUUID}` LIKE '%-%';";
}
queryFixUUIDs = "UPDATE `{TablePlayers}` SET `{FieldUUID}`=? WHERE `{FieldPlayerID}`=?;";
queryGetBPs = "SELECT `{FieldBPOwner}`,`{FieldBPITS}`,`{FieldBPVersion}` FROM `{TableBackpacks}` WHERE `{FieldBPVersion}`<>?;";
queryRewriteBPs = "UPDATE `{TableBackpacks}` SET `{FieldBPITS}`=?,`{FieldBPVersion}`=? WHERE `{FieldBPOwner}`=?;";
updateQuerysForDialect();
@ -220,6 +222,8 @@ protected final void buildQuerys()
queryDeleteOldBackpacks = queryDeleteOldBackpacks.replaceAll("\\{TableBackpacks}", tableBackpacks).replaceAll("\\{FieldBPLastUpdate}", fieldBPLastUpdate).replaceAll("\\{VarMaxAge}", maxAge + "");
queryGetUnsetOrInvalidUUIDs = queryGetUnsetOrInvalidUUIDs.replaceAll("\\{TablePlayers}", tablePlayers).replaceAll("\\{FieldName}", fieldName).replaceAll("\\{FieldUUID}", fieldUUID).replaceAll("\\{FieldPlayerID}", fieldPlayerID);
queryFixUUIDs = queryFixUUIDs.replaceAll("\\{TablePlayers}", tablePlayers).replaceAll("\\{FieldUUID}", fieldUUID).replaceAll("\\{FieldPlayerID}", fieldPlayerID);
queryGetBPs = queryGetBPs.replaceAll("\\{TableBackpacks}", tableBackpacks).replaceAll("\\{FieldBPOwner}", fieldBPOwner).replaceAll("\\{FieldBPITS}", fieldBPITS).replaceAll("\\{FieldBPVersion}", fieldBPVersion);
queryRewriteBPs = queryRewriteBPs.replaceAll("\\{TableBackpacks}", tableBackpacks).replaceAll("\\{FieldBPOwner}", fieldBPOwner).replaceAll("\\{FieldBPITS}", fieldBPITS).replaceAll("\\{FieldBPVersion}", fieldBPVersion);
}
protected abstract void updateQuerysForDialect();
@ -413,4 +417,61 @@ public Backpack loadBackpack(OfflinePlayer player) // The sync function shouldn'
}
return null;
}
private final class Tuple<T, T2>
{
T o1;
T2 o2;
public Tuple(T o1, T2 o2)
{
this.o1 = o1;
this.o2 = o2;
}
public T getO1() { return o1; }
public T2 getO2() { return o2; }
}
@Override
public void rewrite()
{
try(Connection connection = getConnection())
{
List<Tuple<Integer, byte[]>> backpacks = new LinkedList<>();
try(PreparedStatement ps = connection.prepareStatement(queryGetBPs))
{
ps.setInt(1, itsSerializer.getUsedSerializer());
try(ResultSet resultSet = ps.executeQuery())
{
while(resultSet.next())
{
byte[] its = itsSerializer.serialize(itsSerializer.deserialize(resultSet.getBytes(fieldBPITS), resultSet.getInt(fieldBPVersion)));
backpacks.add(new Tuple<>(resultSet.getInt(fieldBPOwner), its));
}
}
}
try(PreparedStatement ps = connection.prepareStatement(queryRewriteBPs))
{
int c = 0;
for(Tuple<Integer, byte[]> backpack : backpacks)
{
ps.setInt(3, backpack.getO1());
ps.setInt(2, itsSerializer.getUsedSerializer());
ps.setBytes(1, backpack.getO2());
ps.addBatch();
if(c++ > 100)
{
ps.executeBatch();
c = 0;
}
}
ps.executeBatch();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2015 GeorgH93
* Copyright (C) 2014-2018 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
@ -18,6 +18,7 @@
package at.pcgamingfreaks.MinePacks;
import at.pcgamingfreaks.MinePacks.Database.Database;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
@ -26,6 +27,7 @@
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import java.util.Collection;
@ -59,7 +61,11 @@ public boolean onCommand(CommandSender sender, Command cmd, String arg, String[]
}
else
{
sender.sendMessage(messageNotFromConsole);
if(args.length > 0 && args[0].equalsIgnoreCase("rewrite"))
{
rewriteDb(sender);
}
else sender.sendMessage(messageNotFromConsole);
return true;
}
if(args.length == 0)
@ -156,6 +162,16 @@ public void onFail()
player.sendMessage(messageNoPermission);
}
break;
case "rewrite":
if(player.hasPermission("backpack.rewrite"))
{
rewriteDb(sender);
}
else
{
player.sendMessage(messageNoPermission);
}
break;
default: // Shows the backpack of an other player
if(player.hasPermission("backpack.others"))
{
@ -170,4 +186,33 @@ public void onFail()
}
return true;
}
void rewriteDb(final CommandSender sender)
{
plugin.log.info("Start backpack rewriting ...");
sender.sendMessage("Start backpack rewriting ...");
plugin.getCommand("backpack").setExecutor(new CommandExecutor() {
@Override
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings)
{
return false;
}
});
plugin.DB.closeAllBackpacks(); // Close the DB connection, we won't need them any longer
HandlerList.unregisterAll(plugin); // Stop the listener, they are no longer needed
plugin.getServer().getScheduler().cancelTasks(plugin); // Kill all still running tasks
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run()
{
plugin.DB.rewrite();
// Enable the plugin again
plugin.getCommand("backpack").setExecutor(new OnCommand(plugin));
plugin.getServer().getPluginManager().registerEvents(new EventListener(plugin), plugin);
if(plugin.config.getFullInvCollect()) (new ItemsCollector(plugin)).runTaskTimer(plugin, plugin.config.getFullInvCheckInterval(), plugin.config.getFullInvCheckInterval());
plugin.log.info("Done backpack rewriting!");
sender.sendMessage("Done backpack rewriting!");
}
});
}
}