add storage migrate command to migrate from one storage type to another
This commit is contained in:
parent
304278edd8
commit
79ede2b586
5 changed files with 204 additions and 3 deletions
|
@ -23,6 +23,7 @@ import lol.pyr.znpcsplus.commands.property.PropertyRemoveCommand;
|
|||
import lol.pyr.znpcsplus.commands.property.PropertySetCommand;
|
||||
import lol.pyr.znpcsplus.commands.storage.ImportCommand;
|
||||
import lol.pyr.znpcsplus.commands.storage.LoadAllCommand;
|
||||
import lol.pyr.znpcsplus.commands.storage.MigrateCommand;
|
||||
import lol.pyr.znpcsplus.commands.storage.SaveAllCommand;
|
||||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.conversion.DataImporterRegistry;
|
||||
|
@ -39,6 +40,7 @@ import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
|
|||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||
import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||
import lol.pyr.znpcsplus.tasks.HologramRefreshTask;
|
||||
import lol.pyr.znpcsplus.tasks.NpcProcessorTask;
|
||||
import lol.pyr.znpcsplus.tasks.ViewableHideOnLeaveListener;
|
||||
|
@ -157,7 +159,7 @@ public class ZNpcsPlus {
|
|||
pluginManager.registerEvents(new UserListener(userManager), bootstrap);
|
||||
|
||||
registerCommands(npcRegistry, skinCache, adventure, actionRegistry,
|
||||
typeRegistry, propertyRegistry, importerRegistry, configManager);
|
||||
typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory);
|
||||
|
||||
log(ChatColor.WHITE + " * Starting tasks...");
|
||||
if (configManager.getConfig().checkForUpdates()) {
|
||||
|
@ -243,7 +245,7 @@ public class ZNpcsPlus {
|
|||
private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure,
|
||||
ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry,
|
||||
EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry,
|
||||
ConfigManager configManager) {
|
||||
ConfigManager configManager, PacketFactory packetFactory) {
|
||||
|
||||
Message<CommandContext> incorrectUsageMessage = context -> context.send(Component.text("Incorrect usage: /" + context.getUsage(), NamedTextColor.RED));
|
||||
CommandManager manager = new CommandManager(bootstrap, adventure, incorrectUsageMessage);
|
||||
|
@ -292,6 +294,7 @@ public class ZNpcsPlus {
|
|||
registerEnumParser(manager, Sound.class, incorrectUsageMessage);
|
||||
registerEnumParser(manager, ArmadilloState.class, incorrectUsageMessage);
|
||||
registerEnumParser(manager, WoldVariant.class, incorrectUsageMessage);
|
||||
registerEnumParser(manager, NpcStorageType.class, incorrectUsageMessage);
|
||||
|
||||
manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root"))
|
||||
.addSubcommand("center", new CenterCommand(npcRegistry))
|
||||
|
@ -316,7 +319,8 @@ public class ZNpcsPlus {
|
|||
.addSubcommand("storage", new MultiCommand(bootstrap.loadHelpMessage("storage"))
|
||||
.addSubcommand("save", new SaveAllCommand(npcRegistry))
|
||||
.addSubcommand("reload", new LoadAllCommand(npcRegistry))
|
||||
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry)))
|
||||
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry))
|
||||
.addSubcommand("migrate", new MigrateCommand(configManager, this, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, npcRegistry.getStorage(), configManager.getConfig().storageType(), npcRegistry)))
|
||||
.addSubcommand("holo", new MultiCommand(bootstrap.loadHelpMessage("holo"))
|
||||
.addSubcommand("add", new HoloAddCommand(npcRegistry))
|
||||
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
package lol.pyr.znpcsplus.commands.storage;
|
||||
|
||||
import lol.pyr.director.adventure.command.CommandContext;
|
||||
import lol.pyr.director.adventure.command.CommandHandler;
|
||||
import lol.pyr.director.common.command.CommandExecutionException;
|
||||
import lol.pyr.znpcsplus.ZNpcsPlus;
|
||||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorage;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MigrateCommand implements CommandHandler {
|
||||
private final ConfigManager configManager;
|
||||
private final ZNpcsPlus plugin;
|
||||
private final PacketFactory packetFactory;
|
||||
private final ActionRegistryImpl actionRegistry;
|
||||
private final NpcTypeRegistryImpl typeRegistry;
|
||||
private final EntityPropertyRegistryImpl propertyRegistry;
|
||||
private final LegacyComponentSerializer textSerializer;
|
||||
private final NpcStorage currentStorage;
|
||||
private final NpcStorageType currentStorageType;
|
||||
private final NpcRegistryImpl npcRegistry;
|
||||
|
||||
public MigrateCommand(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcStorage currentStorage, NpcStorageType currentStorageType, NpcRegistryImpl npcRegistry) {
|
||||
this.configManager = configManager;
|
||||
this.plugin = plugin;
|
||||
this.packetFactory = packetFactory;
|
||||
this.actionRegistry = actionRegistry;
|
||||
this.typeRegistry = typeRegistry;
|
||||
this.propertyRegistry = propertyRegistry;
|
||||
this.textSerializer = textSerializer;
|
||||
this.currentStorage = currentStorage;
|
||||
this.currentStorageType = currentStorageType;
|
||||
this.npcRegistry = npcRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
context.setUsage(context.getLabel() + " storage migrate <from> <to> [force]");
|
||||
NpcStorageType from = context.parse(NpcStorageType.class);
|
||||
NpcStorageType to = context.parse(NpcStorageType.class);
|
||||
boolean force = context.argSize() > 2 && context.parse(Boolean.class);
|
||||
if (from.equals(to)) {
|
||||
context.halt(Component.text("The storage types must be different.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
NpcStorage fromStorage;
|
||||
if (currentStorageType == from) {
|
||||
fromStorage = currentStorage;
|
||||
} else {
|
||||
fromStorage = from.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
if (fromStorage == null) {
|
||||
context.halt(Component.text("Failed to initialize the source storage. Please check the console for more information.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
}
|
||||
Collection<NpcEntryImpl> entries;
|
||||
try {
|
||||
entries = fromStorage.loadNpcs();
|
||||
} catch (Exception e) {
|
||||
context.halt(Component.text("Failed to load NPCs from the source storage.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
if (entries.isEmpty()) {
|
||||
context.send(Component.text("No NPCs to migrate.", NamedTextColor.YELLOW));
|
||||
return;
|
||||
}
|
||||
NpcStorage toStorage;
|
||||
if (currentStorageType == to) {
|
||||
toStorage = currentStorage;
|
||||
} else {
|
||||
toStorage = to.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
if (toStorage == null) {
|
||||
context.halt(Component.text("Failed to initialize the destination storage. Please check the console for more information.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Collection<NpcEntryImpl> existingEntries;
|
||||
try {
|
||||
existingEntries = toStorage.loadNpcs();
|
||||
} catch (Exception e) {
|
||||
context.halt(Component.text("Failed to load NPCs from the destination storage.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
if (existingEntries.isEmpty()) {
|
||||
toStorage.saveNpcs(entries);
|
||||
context.send(Component.text("Migrated " + entries.size() + " NPCs from the source storage (", NamedTextColor.GREEN)
|
||||
.append(Component.text(from.name(), NamedTextColor.GOLD))
|
||||
.append(Component.text(") to the destination storage (", NamedTextColor.GREEN))
|
||||
.append(Component.text(to.name(), NamedTextColor.GOLD))
|
||||
.append(Component.text(").", NamedTextColor.GREEN)));
|
||||
if (currentStorageType == to) {
|
||||
npcRegistry.reload();
|
||||
} else {
|
||||
toStorage.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!force) {
|
||||
Collection<NpcEntryImpl> toSave = entries.stream().filter(e -> existingEntries.stream().noneMatch(e2 -> e2.getId().equals(e.getId()))).collect(Collectors.toList());
|
||||
Collection<NpcEntryImpl> idExists = entries.stream().filter(e -> existingEntries.stream().anyMatch(e2 -> e2.getId().equals(e.getId()))).collect(Collectors.toList());
|
||||
if (toSave.isEmpty()) {
|
||||
context.send(Component.text("No NPCs to migrate.", NamedTextColor.YELLOW));
|
||||
if (currentStorageType != to) {
|
||||
toStorage.close();
|
||||
}
|
||||
} else {
|
||||
toStorage.saveNpcs(toSave);
|
||||
context.send(Component.text("Migrated " + toSave.size() + " NPCs from the source storage (", NamedTextColor.GREEN)
|
||||
.append(Component.text(from.name(), NamedTextColor.GOLD))
|
||||
.append(Component.text(") to the destination storage (", NamedTextColor.GREEN))
|
||||
.append(Component.text(to.name(), NamedTextColor.GOLD))
|
||||
.append(Component.text(").", NamedTextColor.GREEN)));
|
||||
if (currentStorageType == to) {
|
||||
npcRegistry.reload();
|
||||
} else {
|
||||
toStorage.close();
|
||||
}
|
||||
}
|
||||
if (!idExists.isEmpty()) {
|
||||
AtomicReference<Component> component = new AtomicReference<>(Component.text("The following NPCs were not migrated because their IDs already exist in the destination storage:").color(NamedTextColor.YELLOW));
|
||||
idExists.forEach(e -> {
|
||||
component.set(component.get().append(Component.newline()).append(Component.text(e.getId(), NamedTextColor.RED)));
|
||||
});
|
||||
component.set(component.get().append(Component.newline())
|
||||
.append(Component.text("Use the ", NamedTextColor.YELLOW))
|
||||
.append(Component.text("force", NamedTextColor.GOLD))
|
||||
.append(Component.text(" argument to overwrite them.", NamedTextColor.YELLOW)));
|
||||
context.send(component.get());
|
||||
}
|
||||
} else {
|
||||
toStorage.saveNpcs(entries);
|
||||
context.send(Component.text("Force migrated " + entries.size() + " NPCs from the source storage (", NamedTextColor.GREEN)
|
||||
.append(Component.text(from.name(), NamedTextColor.GOLD))
|
||||
.append(Component.text(") to the destination storage (", NamedTextColor.GREEN))
|
||||
.append(Component.text(to.name(), NamedTextColor.GOLD))
|
||||
.append(Component.text(").", NamedTextColor.GREEN)));
|
||||
if (currentStorageType == to) {
|
||||
npcRegistry.reload();
|
||||
} else {
|
||||
toStorage.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
||||
if (context.argSize() == 1) {
|
||||
return context.suggestEnum(NpcStorageType.values());
|
||||
} else if (context.argSize() == 2) {
|
||||
NpcStorageType from = context.suggestionParse(0, NpcStorageType.class);
|
||||
if (from == null) return Collections.emptyList();
|
||||
return context.suggestCollection(Arrays.stream(NpcStorageType.values())
|
||||
.filter(t -> t != from).map(Enum::name).collect(Collectors.toList()));
|
||||
} else if (context.argSize() == 3) {
|
||||
return context.suggestLiteral("true");
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -209,4 +209,8 @@ public class NpcRegistryImpl implements NpcRegistry {
|
|||
npcList.forEach(npcEntry -> npcEntry.getNpc().delete());
|
||||
storage.close();
|
||||
}
|
||||
|
||||
public NpcStorage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
}
|
||||
|
|
16
plugin/src/main/resources/messages/storage-hover/migrate.txt
Normal file
16
plugin/src/main/resources/messages/storage-hover/migrate.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
<gray>Usage <gold>» <yellow>/npc storage migrate <gold><from> <to> [force]
|
||||
|
||||
<gray>Storage Types:
|
||||
<gold>* <yellow>YAML <gray>- Npcs are stored in yaml files
|
||||
<gold>* <yellow>SQLite <gray>- Npcs are stored in a SQLite database
|
||||
<gold>* <yellow>MySQL <gray>- Npcs are stored in a MySQL database
|
||||
|
||||
<gray>Command used to migrate npcs from one storage type to another.
|
||||
|
||||
This command will NOT delete the original storage files or database,
|
||||
but will copy the npcs to the new storage type.
|
||||
|
||||
<gray>This will also not overwrite any existing npcs in the new storage
|
||||
type, unless the <gold>force <gray>argument is set to <gold>true<gray>.
|
||||
<red>Warning: <bold>force</bold> will overwrite any existing npcs with the same id
|
||||
in the new storage type and CANNOT be undone.
|
|
@ -5,4 +5,5 @@
|
|||
<hover:show_text:'{@storage-hover/save}'><gold>* <yellow>/npc storage save</hover>
|
||||
<hover:show_text:'{@storage-hover/reload}'><gold>* <yellow>/npc storage reload</hover>
|
||||
<hover:show_text:'{@storage-hover/import}'><gold>* <yellow>/npc storage import <importer></hover>
|
||||
<hover:show_text:'{@storage-hover/migrate}'><gold>* <yellow>/npc storage migrate <from> <to> [force]</hover>
|
||||
|
||||
|
|
Loading…
Reference in a new issue