Remove legacy import functionality

Users wishing to import data from the old export formats will need to downgrade LuckPerms to a version before this commit, import their data, and then upgrade again.
This commit is contained in:
Luck 2020-03-03 11:20:32 +00:00
parent d475ace76c
commit 85451a9a20
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
8 changed files with 19 additions and 452 deletions

View File

@ -29,15 +29,12 @@ import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.commands.log.LogNotify;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.message.Message;
import me.lucko.luckperms.common.messaging.InternalMessagingService;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import net.luckperms.api.event.log.LogBroadcastEvent;
import net.luckperms.api.event.log.LogNotifyEvent;
import java.util.Optional;
public class LogDispatcher {
private final LuckPermsPlugin plugin;
@ -61,20 +58,11 @@ public class LogDispatcher {
}
public void dispatch(LoggedAction entry, Sender sender) {
// set the event to cancelled if the sender is import
if (!this.plugin.getEventDispatcher().dispatchLogPublish(sender.isImport(), entry)) {
if (!this.plugin.getEventDispatcher().dispatchLogPublish(false, entry)) {
this.plugin.getStorage().logAction(entry);
}
// don't dispatch log entries sent by an import process
if (sender.isImport()) {
return;
}
Optional<InternalMessagingService> messagingService = this.plugin.getMessagingService();
if (!sender.isImport() && messagingService.isPresent()) {
messagingService.get().pushLog(entry);
}
this.plugin.getMessagingService().ifPresent(service -> service.pushLog(entry));
boolean shouldCancel = !this.plugin.getConfiguration().get(ConfigKeys.LOG_NOTIFY);
if (!this.plugin.getEventDispatcher().dispatchLogBroadcast(shouldCancel, entry, LogBroadcastEvent.Origin.LOCAL)) {

View File

@ -1,340 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.backup;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.command.CommandManager;
import me.lucko.luckperms.common.command.CommandResult;
import me.lucko.luckperms.common.command.utils.ArgumentTokenizer;
import me.lucko.luckperms.common.locale.message.Message;
import me.lucko.luckperms.common.sender.DummySender;
import me.lucko.luckperms.common.sender.Sender;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* Handles import operations
*/
public class LegacyImporter implements Runnable {
private final CommandManager commandManager;
private final Set<Sender> notify;
private final List<String> commandList;
private final List<ImportCommand> commands;
public LegacyImporter(CommandManager commandManager, Sender executor, List<String> commands) {
this.commandManager = commandManager;
if (executor.isConsole()) {
this.notify = ImmutableSet.of(executor);
} else {
this.notify = ImmutableSet.of(executor, commandManager.getPlugin().getConsoleSender());
}
this.commandList = commands.stream()
.map(String::trim)
.filter(s -> !s.isEmpty())
.filter(s -> !s.startsWith("#"))
.filter(s -> !s.startsWith("//"))
.map(s -> s.startsWith("/luckperms ") ? s.substring("/luckperms ".length()) : s)
.map(s -> s.startsWith("/lp ") ? s.substring("/lp ".length()) : s)
.collect(Collectors.toList());
this.commands = new ArrayList<>();
}
@Override
public void run() {
long startTime = System.currentTimeMillis();
this.notify.forEach(s -> Message.IMPORT_START.send(s));
// start an update task in the background - we'll #join this later
CompletableFuture<Void> updateTask = CompletableFuture.runAsync(() -> this.commandManager.getPlugin().getSyncTaskBuffer().requestDirectly());
this.notify.forEach(s -> Message.IMPORT_INFO.send(s, "Processing commands..."));
// form instances for all commands, and register them
int index = 1;
for (String command : this.commandList) {
ImportCommand cmd = new ImportCommand(this.commandManager, index, command);
this.commands.add(cmd);
if (cmd.getCommand().startsWith("creategroup ") || cmd.getCommand().startsWith("createtrack ")) {
cmd.process(); // process immediately
}
index++;
}
// split data up into sections for each holder
// holder id --> commands
ListMultimap<String, ImportCommand> sections = MultimapBuilder.linkedHashKeys().arrayListValues().build();
for (ImportCommand cmd : this.commands) {
sections.put(Strings.nullToEmpty(cmd.getTarget()), cmd);
}
this.notify.forEach(s -> Message.IMPORT_INFO.send(s, "Waiting for initial update task to complete..."));
// join the update task future before scheduling command executions
updateTask.join();
this.notify.forEach(s -> Message.IMPORT_INFO.send(s, "Setting up command executor..."));
// create a threadpool for the processing
ExecutorService executor = Executors.newFixedThreadPool(128, new ThreadFactoryBuilder().setNameFormat("luckperms-importer-%d").build());
// A set of futures, which are really just the processes we need to wait for.
Set<CompletableFuture<Void>> futures = new HashSet<>();
AtomicInteger processedCount = new AtomicInteger(0);
// iterate through each user sublist.
for (Collection<ImportCommand> subList : sections.asMap().values()) {
// register and start a new thread to process the sublist
futures.add(CompletableFuture.completedFuture(subList).thenAcceptAsync(sl -> {
// iterate through each user in the sublist, and grab their data.
for (ImportCommand cmd : sl) {
cmd.process();
processedCount.incrementAndGet();
}
}, executor));
}
// all of the threads have been scheduled now and are running. we just need to wait for them all to complete
CompletableFuture<Void> overallFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
this.notify.forEach(s -> Message.IMPORT_INFO.send(s, "All commands have been processed and scheduled - now waiting for the execution to complete."));
while (true) {
try {
overallFuture.get(2, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) {
// abnormal error - just break
e.printStackTrace();
break;
} catch (TimeoutException e) {
// still executing - send a progress report and continue waiting
sendProgress(processedCount.get());
continue;
}
// process is complete
break;
}
executor.shutdown();
long endTime = System.currentTimeMillis();
double seconds = (endTime - startTime) / 1000.0;
int errors = (int) this.commands.stream().filter(v -> v.getResult().wasFailure()).count();
switch (errors) {
case 0:
this.notify.forEach(s -> Message.IMPORT_END_COMPLETE.send(s, seconds));
break;
case 1:
this.notify.forEach(s -> Message.IMPORT_END_COMPLETE_ERR_SIN.send(s, seconds, errors));
break;
default:
this.notify.forEach(s -> Message.IMPORT_END_COMPLETE_ERR.send(s, seconds, errors));
break;
}
AtomicInteger errIndex = new AtomicInteger(1);
for (ImportCommand e : this.commands) {
if (e.getResult() != null && e.getResult().wasFailure()) {
for (Sender s : this.notify) {
Message.IMPORT_END_ERROR_HEADER.send(s, errIndex.get(), e.getId(), e.getCommand(), e.getResult().toString());
e.getOutput().forEach(out -> Message.IMPORT_END_ERROR_CONTENT.send(s, out));
Message.IMPORT_END_ERROR_FOOTER.send(s);
}
errIndex.incrementAndGet();
}
}
}
private void sendProgress(int processedCount) {
int percent = (processedCount * 100) / this.commandList.size();
int errors = (int) this.commands.stream().filter(v -> v.isCompleted() && v.getResult().wasFailure()).count();
if (errors == 1) {
this.notify.forEach(s -> Message.IMPORT_PROGRESS_SIN.send(s, percent, processedCount, this.commands.size(), errors));
} else {
this.notify.forEach(s -> Message.IMPORT_PROGRESS.send(s, percent, processedCount, this.commands.size(), errors));
}
}
private static class ImportCommand extends DummySender {
private static final Splitter SPACE_SPLITTER = Splitter.on(" ");
private final CommandManager commandManager;
private final int id;
private final String command;
private final String target;
private boolean completed = false;
private final List<String> output = new ArrayList<>();
private CommandResult result = CommandResult.FAILURE;
ImportCommand(CommandManager commandManager, int id, String command) {
super(commandManager.getPlugin(), Sender.IMPORT_UUID, Sender.IMPORT_NAME);
this.commandManager = commandManager;
this.id = id;
this.command = command;
this.target = determineTarget(command);
}
@Override
protected void consumeMessage(String s) {
this.output.add(s);
}
public void process() {
if (isCompleted()) {
return;
}
try {
List<String> args = ArgumentTokenizer.EXECUTE.tokenizeInput(getCommand());
// rewrite rule for switchprimarygroup command
if (args.size() >= 3 && args.get(0).equals("user") && args.get(2).equals("switchprimarygroup")) {
args.remove(2);
args.add(2, "parent");
args.add(3, "switchprimarygroup");
}
CommandResult result = this.commandManager.executeCommand(this, "lp", args, Runnable::run).get();
setResult(result);
} catch (Exception e) {
setResult(CommandResult.FAILURE);
e.printStackTrace();
}
setCompleted(true);
}
private static String determineTarget(String command) {
if (command.startsWith("user ") && command.length() > "user ".length()) {
String subCmd = command.substring("user ".length());
if (!subCmd.contains(" ")) {
return null;
}
String targetUser = SPACE_SPLITTER.split(subCmd).iterator().next();
return "u:" + targetUser;
}
if (command.startsWith("group ") && command.length() > "group ".length()) {
String subCmd = command.substring("group ".length());
if (!subCmd.contains(" ")) {
return null;
}
String targetGroup = SPACE_SPLITTER.split(subCmd).iterator().next();
return "g:" + targetGroup;
}
if (command.startsWith("track ") && command.length() > "track ".length()) {
String subCmd = command.substring("track ".length());
if (!subCmd.contains(" ")) {
return null;
}
String targetTrack = SPACE_SPLITTER.split(subCmd).iterator().next();
return "t:" + targetTrack;
}
if (command.startsWith("creategroup ") && command.length() > "creategroup ".length()) {
String targetGroup = command.substring("creategroup ".length());
return "g:" + targetGroup;
}
if (command.startsWith("createtrack ") && command.length() > "createtrack ".length()) {
String targetTrack = command.substring("createtrack ".length());
return "t:" + targetTrack;
}
return null;
}
public int getId() {
return this.id;
}
public String getCommand() {
return this.command;
}
public String getTarget() {
return this.target;
}
public boolean isCompleted() {
return this.completed;
}
public List<String> getOutput() {
return this.output;
}
public CommandResult getResult() {
return this.result;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public void setResult(CommandResult result) {
this.result = result;
}
}
}

View File

@ -74,7 +74,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
@ -139,10 +138,6 @@ public class CommandManager {
}
public CompletableFuture<CommandResult> executeCommand(Sender sender, String label, List<String> args) {
return executeCommand(sender, label, args, this.executor);
}
public CompletableFuture<CommandResult> executeCommand(Sender sender, String label, List<String> args, Executor executor) {
return CompletableFuture.supplyAsync(() -> {
try {
return execute(sender, label, args);
@ -151,7 +146,7 @@ public class CommandManager {
e.printStackTrace();
return null;
}
}, executor);
}, this.executor);
}
public boolean hasPermissionForAny(Sender sender) {

View File

@ -45,15 +45,6 @@ public final class StorageAssistant {
private StorageAssistant() {}
public static Group loadGroup(String target, Sender sender, LuckPermsPlugin plugin, boolean auditTemporary) {
// special handling for the importer
if (sender.isImport()) {
Group group = plugin.getGroupManager().getIfLoaded(target);
if (group == null) {
Message.GROUP_NOT_FOUND.send(sender, target);
}
return group;
}
Group group = plugin.getStorage().loadGroup(target).join().orElse(null);
if (group == null) {
// failed to load, but it might be a display name.
@ -77,15 +68,7 @@ public final class StorageAssistant {
}
public static Track loadTrack(String target, Sender sender, LuckPermsPlugin plugin) {
Track track;
// special handling for the importer
if (sender.isImport()) {
track = plugin.getTrackManager().getIfLoaded(target);
} else {
track = plugin.getStorage().loadTrack(target).join().orElse(null);
}
Track track = plugin.getStorage().loadTrack(target).join().orElse(null);
if (track == null) {
Message.TRACK_NOT_FOUND.send(sender, target);
return null;
@ -95,13 +78,6 @@ public final class StorageAssistant {
}
public static void save(User user, Sender sender, LuckPermsPlugin plugin) {
// special handling for the importer
if (sender.isImport()) {
// join calls to save users - as we always load them
plugin.getStorage().saveUser(user).join();
return;
}
try {
plugin.getStorage().saveUser(user).get();
} catch (Exception e) {
@ -117,13 +93,6 @@ public final class StorageAssistant {
}
public static void save(Group group, Sender sender, LuckPermsPlugin plugin) {
// special handling for the importer
if (sender.isImport()) {
// allow the buffer to handle things
plugin.getStorage().saveGroup(group);
return;
}
try {
plugin.getStorage().saveGroup(group).get();
} catch (Exception e) {
@ -142,13 +111,6 @@ public final class StorageAssistant {
}
public static void save(Track track, Sender sender, LuckPermsPlugin plugin) {
// special handling for the importer
if (sender.isImport()) {
// allow the buffer to handle things
plugin.getStorage().saveTrack(track);
return;
}
try {
plugin.getStorage().saveTrack(track).get();
} catch (Exception e) {

View File

@ -87,7 +87,7 @@ public class LogNotify extends ChildCommand<Log> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Log log, List<String> args, String label) {
if (sender.isConsole() || sender.isImport()) {
if (sender.isConsole()) {
Message.LOG_NOTIFY_CONSOLE.send(sender);
return CommandResult.SUCCESS;
}

View File

@ -28,7 +28,6 @@ package me.lucko.luckperms.common.commands.misc;
import com.google.gson.JsonObject;
import me.lucko.luckperms.common.backup.Importer;
import me.lucko.luckperms.common.backup.LegacyImporter;
import me.lucko.luckperms.common.command.CommandResult;
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
@ -48,7 +47,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.zip.GZIPInputStream;
public class ImportCommand extends SingleCommand {
@ -92,46 +90,22 @@ public class ImportCommand extends SingleCommand {
return CommandResult.FAILURE;
}
if (path.getFileName().toString().endsWith(".json.gz")) {
return exportModern(plugin, sender, path);
} else {
return exportLegacy(plugin, sender, path);
}
}
private CommandResult exportModern(LuckPermsPlugin plugin, Sender sender, Path path) {
JsonObject data;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))) {
data = GsonProvider.normal().fromJson(reader, JsonObject.class);;
} catch (IOException e) {
e.printStackTrace();
Message.IMPORT_FILE_READ_FAILURE.send(sender);
return CommandResult.FAILURE;
}
return runImporter(plugin, sender, () -> new Importer(plugin, sender, data));
}
private CommandResult exportLegacy(LuckPermsPlugin plugin, Sender sender, Path path) {
List<String> commands;
try {
commands = Files.readAllLines(path, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
Message.IMPORT_FILE_READ_FAILURE.send(sender);
return CommandResult.FAILURE;
}
return runImporter(plugin, sender, () -> new LegacyImporter(plugin.getCommandManager(), sender, commands));
}
private CommandResult runImporter(LuckPermsPlugin plugin, Sender sender, Supplier<Runnable> importerSupplier) {
if (!this.running.compareAndSet(false, true)) {
Message.IMPORT_ALREADY_RUNNING.send(sender);
return CommandResult.STATE_ERROR;
}
Runnable importer = importerSupplier.get();
JsonObject data;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))) {
data = GsonProvider.normal().fromJson(reader, JsonObject.class);
} catch (IOException e) {
e.printStackTrace();
Message.IMPORT_FILE_READ_FAILURE.send(sender);
this.running.set(false);
return CommandResult.FAILURE;
}
Importer importer = new Importer(plugin, sender, data);
// Run the importer in its own thread.
plugin.getBootstrap().getScheduler().executeAsync(() -> {
@ -144,4 +118,5 @@ public class ImportCommand extends SingleCommand {
return CommandResult.SUCCESS;
}
}

View File

@ -457,7 +457,7 @@ public enum Message {
FILE_NOT_WITHIN_DIRECTORY("&cError: File &4{}&c must be a direct child of the data directory.", true),
IMPORT_FILE_DOESNT_EXIST("&cError: File &4{}&c does not exist.", true),
IMPORT_FILE_NOT_READABLE("&cError: File &4{}&c is not readable.", true),
IMPORT_FILE_READ_FAILURE("&cAn unexpected error occured whilst reading from the import file.", true),
IMPORT_FILE_READ_FAILURE("&cAn unexpected error occured whilst reading from the import file. (is it the correct format?)", true),
IMPORT_PROGRESS("&b(Import) &b-> &f{}&f% complete &7- &b{}&f/&b{} &foperations complete with &c{} &ferrors.", true),
IMPORT_PROGRESS_SIN("&b(Import) &b-> &f{}&f% complete &7- &b{}&f/&b{} &foperations complete with &c{} &ferror.", true),

View File

@ -48,10 +48,6 @@ public interface Sender {
UUID CONSOLE_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
/** The name used by the console sender. */
String CONSOLE_NAME = "Console";
/** The uuid used by the 'import' sender. */
UUID IMPORT_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111");
/** The name used by the 'import' sender. */
String IMPORT_NAME = "Import";
/**
* Gets the plugin instance the sender is from.
@ -157,16 +153,7 @@ public interface Sender {
* @return if the sender is the console
*/
default boolean isConsole() {
return CONSOLE_UUID.equals(getUniqueId()) || IMPORT_UUID.equals(getUniqueId());
}
/**
* Gets whether this sender is an import process
*
* @return if the sender is an import process
*/
default boolean isImport() {
return IMPORT_UUID.equals(getUniqueId());
return CONSOLE_UUID.equals(getUniqueId());
}
/**