Search in sources :

Example 1 with Storage

use of me.lucko.luckperms.common.storage.Storage in project LuckPerms by lucko.

the class Exporter method run.

@Override
public void run() {
    try (BufferedWriter writer = Files.newBufferedWriter(this.filePath, StandardCharsets.UTF_8)) {
        this.log.log("Starting.");
        write(writer, "# LuckPerms Export File");
        write(writer, "# Generated by " + this.executor.getNameWithLocation() + " at " + DATE_FORMAT.format(new Date(System.currentTimeMillis())));
        write(writer, "");
        // Export Groups
        this.log.log("Starting group export.");
        // Create the actual groups first
        write(writer, "# Create groups");
        AtomicInteger groupCount = new AtomicInteger(0);
        List<? extends Group> groups = this.plugin.getGroupManager().getAll().values().stream().sorted((o1, o2) -> {
            int i = Integer.compare(o2.getWeight().orElse(0), o1.getWeight().orElse(0));
            return i != 0 ? i : o1.getName().compareToIgnoreCase(o2.getName());
        }).collect(Collectors.toList());
        for (Group group : groups) {
            if (!group.getName().equals(NodeFactory.DEFAULT_GROUP_NAME)) {
                write(writer, "/lp creategroup " + group.getName());
            }
        }
        for (Group group : groups) {
            if (groupCount.get() == 0) {
                write(writer, "");
            }
            write(writer, "# Export group: " + group.getName());
            for (Node node : group.getEnduringNodes().values()) {
                write(writer, "/lp " + NodeFactory.nodeAsCommand(node, group.getName(), HolderType.GROUP, true));
            }
            write(writer, "");
            this.log.logAllProgress("Exported {} groups so far.", groupCount.incrementAndGet());
        }
        this.log.log("Exported " + groupCount.get() + " groups.");
        write(writer, "");
        write(writer, "");
        // Export tracks
        this.log.log("Starting track export.");
        Collection<? extends Track> tracks = this.plugin.getTrackManager().getAll().values();
        if (!tracks.isEmpty()) {
            // Create the actual tracks first
            write(writer, "# Create tracks");
            for (Track track : tracks) {
                write(writer, "/lp createtrack " + track.getName());
            }
            write(writer, "");
            AtomicInteger trackCount = new AtomicInteger(0);
            for (Track track : this.plugin.getTrackManager().getAll().values()) {
                write(writer, "# Export track: " + track.getName());
                for (String group : track.getGroups()) {
                    write(writer, "/lp track " + track.getName() + " append " + group);
                }
                write(writer, "");
                this.log.logAllProgress("Exported {} tracks so far.", trackCount.incrementAndGet());
            }
            write(writer, "");
            write(writer, "");
        }
        this.log.log("Exported " + tracks.size() + " tracks.");
        // Users are migrated in separate threads.
        // This is because there are likely to be a lot of them, and because we can.
        // It's a big speed improvement, since the database/files are split up and can handle concurrent reads.
        this.log.log("Starting user export. Finding a list of unique users to export.");
        // Find all of the unique users we need to export
        Storage ds = this.plugin.getStorage();
        Set<UUID> users = ds.getUniqueUsers().join();
        this.log.log("Found " + users.size() + " unique users to export.");
        write(writer, "# Export users");
        // divide into 16 pools.
        Cycle<List<UUID>> userPools = new Cycle<>(nInstances(32, ArrayList::new));
        for (UUID uuid : users) {
            userPools.next().add(uuid);
        }
        this.log.log("Split users into " + userPools.getBacking().size() + " threads for export.");
        // Setup a file writing lock. We don't want multiple threads writing at the same time.
        // The write function accepts a list of strings, as we want a user's data to be grouped together.
        // This means it can be processed and added in one go.
        ReentrantLock lock = new ReentrantLock();
        Consumer<List<String>> writeFunction = strings -> {
            lock.lock();
            try {
                for (String s : strings) {
                    write(writer, s);
                }
            } finally {
                lock.unlock();
            }
        };
        // A set of futures, which are really just the threads we need to wait for.
        Set<CompletableFuture<Void>> futures = new HashSet<>();
        AtomicInteger userCount = new AtomicInteger(0);
        // iterate through each user sublist.
        for (List<UUID> subList : userPools.getBacking()) {
            // register and start a new thread to process the sublist
            futures.add(CompletableFuture.runAsync(() -> {
                // iterate through each user in the sublist, and grab their data.
                for (UUID uuid : subList) {
                    try {
                        // actually export the user. this output will be fed to the writing function when we have all of the user's data.
                        List<String> output = new ArrayList<>();
                        User user = this.plugin.getStorage().loadUser(uuid, null).join();
                        output.add("# Export user: " + user.getUuid().toString() + " - " + user.getName().orElse("unknown username"));
                        boolean inDefault = false;
                        for (Node node : user.getEnduringNodes().values()) {
                            if (node.isGroupNode() && node.getGroupName().equalsIgnoreCase(NodeFactory.DEFAULT_GROUP_NAME)) {
                                inDefault = true;
                                continue;
                            }
                            output.add("/lp " + NodeFactory.nodeAsCommand(node, user.getUuid().toString(), HolderType.USER, true));
                        }
                        if (!user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME).equalsIgnoreCase(NodeFactory.DEFAULT_GROUP_NAME)) {
                            output.add("/lp user " + user.getUuid().toString() + " switchprimarygroup " + user.getPrimaryGroup().getStoredValue().get());
                        }
                        if (!inDefault) {
                            output.add("/lp user " + user.getUuid().toString() + " parent remove default");
                        }
                        this.plugin.getUserManager().cleanup(user);
                        writeFunction.accept(output);
                        this.log.logProgress("Exported {} users so far.", userCount.incrementAndGet());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, this.plugin.getBootstrap().getScheduler().async()));
        }
        // all of the threads have been scheduled now and are running. we just need to wait for them all to complete
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();
        this.log.log("Exported " + userCount.get() + " users.");
        writer.flush();
        this.log.getListeners().forEach(l -> Message.LOG_EXPORT_SUCCESS.send(l, this.filePath.toFile().getAbsolutePath()));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Also used : Cycle(me.lucko.luckperms.common.utils.Cycle) Date(java.util.Date) SimpleDateFormat(java.text.SimpleDateFormat) CompletableFuture(java.util.concurrent.CompletableFuture) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) ProgressLogger(me.lucko.luckperms.common.logging.ProgressLogger) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Message(me.lucko.luckperms.common.locale.message.Message) HolderType(me.lucko.luckperms.common.references.HolderType) Storage(me.lucko.luckperms.common.storage.Storage) LuckPermsPlugin(me.lucko.luckperms.common.plugin.LuckPermsPlugin) Path(java.nio.file.Path) ReentrantLock(java.util.concurrent.locks.ReentrantLock) Files(java.nio.file.Files) BufferedWriter(java.io.BufferedWriter) Collection(java.util.Collection) Set(java.util.Set) IOException(java.io.IOException) NodeFactory(me.lucko.luckperms.common.node.NodeFactory) Sender(me.lucko.luckperms.common.sender.Sender) UUID(java.util.UUID) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) Consumer(java.util.function.Consumer) Track(me.lucko.luckperms.common.model.Track) List(java.util.List) Group(me.lucko.luckperms.common.model.Group) Node(me.lucko.luckperms.api.Node) User(me.lucko.luckperms.common.model.User) Group(me.lucko.luckperms.common.model.Group) User(me.lucko.luckperms.common.model.User) Node(me.lucko.luckperms.api.Node) BufferedWriter(java.io.BufferedWriter) CompletableFuture(java.util.concurrent.CompletableFuture) ArrayList(java.util.ArrayList) List(java.util.List) UUID(java.util.UUID) HashSet(java.util.HashSet) ReentrantLock(java.util.concurrent.locks.ReentrantLock) Date(java.util.Date) Cycle(me.lucko.luckperms.common.utils.Cycle) IOException(java.io.IOException) Storage(me.lucko.luckperms.common.storage.Storage) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Track(me.lucko.luckperms.common.model.Track)

Aggregations

BufferedWriter (java.io.BufferedWriter)1 IOException (java.io.IOException)1 StandardCharsets (java.nio.charset.StandardCharsets)1 Files (java.nio.file.Files)1 Path (java.nio.file.Path)1 SimpleDateFormat (java.text.SimpleDateFormat)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 Date (java.util.Date)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Set (java.util.Set)1 UUID (java.util.UUID)1 CompletableFuture (java.util.concurrent.CompletableFuture)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1 ReentrantLock (java.util.concurrent.locks.ReentrantLock)1 Consumer (java.util.function.Consumer)1 Supplier (java.util.function.Supplier)1 Collectors (java.util.stream.Collectors)1 Node (me.lucko.luckperms.api.Node)1