001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.backup;
020
021import com.google.common.cache.Cache;
022import com.google.common.cache.CacheBuilder;
023import com.google.inject.Inject;
024import com.google.inject.Singleton;
025import com.plotsquared.core.PlotSquared;
026import com.plotsquared.core.configuration.Settings;
027import com.plotsquared.core.configuration.caption.Templates;
028import com.plotsquared.core.configuration.caption.TranslatableCaption;
029import com.plotsquared.core.inject.factory.PlayerBackupProfileFactory;
030import com.plotsquared.core.player.PlotPlayer;
031import com.plotsquared.core.plot.Plot;
032import com.plotsquared.core.util.task.TaskManager;
033import net.kyori.adventure.text.minimessage.Template;
034import org.checkerframework.checker.nullness.qual.NonNull;
035import org.checkerframework.checker.nullness.qual.Nullable;
036
037import java.nio.file.Files;
038import java.nio.file.Path;
039import java.util.Objects;
040import java.util.concurrent.ExecutionException;
041import java.util.concurrent.TimeUnit;
042
043/**
044 * {@inheritDoc}
045 */
046@Singleton
047public class SimpleBackupManager implements BackupManager {
048
049    private final Path backupPath;
050    private final boolean automaticBackup;
051    private final int backupLimit;
052    private final Cache<PlotCacheKey, BackupProfile> backupProfileCache = CacheBuilder.newBuilder()
053            .expireAfterAccess(3, TimeUnit.MINUTES).build();
054    private final PlayerBackupProfileFactory playerBackupProfileFactory;
055
056    @Inject
057    public SimpleBackupManager(final @NonNull PlayerBackupProfileFactory playerBackupProfileFactory) throws Exception {
058        this.playerBackupProfileFactory = playerBackupProfileFactory;
059        this.backupPath = Objects.requireNonNull(PlotSquared.platform()).getDirectory().toPath().resolve("backups");
060        if (!Files.exists(backupPath)) {
061            Files.createDirectory(backupPath);
062        }
063        this.automaticBackup = Settings.Backup.AUTOMATIC_BACKUPS;
064        this.backupLimit = Settings.Backup.BACKUP_LIMIT;
065    }
066
067    public SimpleBackupManager(
068            final Path backupPath, final boolean automaticBackup,
069            final int backupLimit, final PlayerBackupProfileFactory playerBackupProfileFactory
070    ) {
071        this.backupPath = backupPath;
072        this.automaticBackup = automaticBackup;
073        this.backupLimit = backupLimit;
074        this.playerBackupProfileFactory = playerBackupProfileFactory;
075    }
076
077    @Override
078    public @NonNull BackupProfile getProfile(final @NonNull Plot plot) {
079        if (plot.hasOwner()) {
080            try {
081                return backupProfileCache.get(
082                        new PlotCacheKey(plot),
083                        () -> this.playerBackupProfileFactory.create(plot.getOwnerAbs(), plot)
084                );
085            } catch (ExecutionException e) {
086                final BackupProfile profile = this.playerBackupProfileFactory.create(plot.getOwnerAbs(), plot);
087                this.backupProfileCache.put(new PlotCacheKey(plot), profile);
088                return profile;
089            }
090        }
091        return new NullBackupProfile();
092    }
093
094    @Override
095    public void automaticBackup(@Nullable PlotPlayer<?> player, final @NonNull Plot plot, @NonNull Runnable whenDone) {
096        final BackupProfile profile;
097        if (!this.shouldAutomaticallyBackup() || (profile = getProfile(plot)) instanceof NullBackupProfile) {
098            whenDone.run();
099        } else {
100            if (player != null) {
101                player.sendMessage(
102                        TranslatableCaption.of("backups.backup_automatic_started"),
103                        Template.of("plot", plot.getId().toString())
104                );
105            }
106            profile.createBackup().whenComplete((backup, throwable) -> {
107                if (throwable != null) {
108                    if (player != null) {
109                        player.sendMessage(
110                                TranslatableCaption.of("backups.backup_automatic_failure"),
111                                Templates.of("reason", throwable.getMessage())
112                        );
113                    }
114                    throwable.printStackTrace();
115                } else {
116                    if (player != null) {
117                        player.sendMessage(TranslatableCaption.of("backups.backup_automatic_finished"));
118                        TaskManager.runTaskAsync(whenDone);
119                    }
120                }
121            });
122        }
123    }
124
125    @Override
126    public boolean shouldAutomaticallyBackup() {
127        return this.automaticBackup;
128    }
129
130    public Path getBackupPath() {
131        return this.backupPath;
132    }
133
134    public int getBackupLimit() {
135        return this.backupLimit;
136    }
137
138    private static final class PlotCacheKey {
139
140        private final Plot plot;
141
142        private PlotCacheKey(Plot plot) {
143            this.plot = plot;
144        }
145
146        @Override
147        public boolean equals(final Object o) {
148            if (this == o) {
149                return true;
150            }
151            if (o == null || getClass() != o.getClass()) {
152                return false;
153            }
154            final PlotCacheKey that = (PlotCacheKey) o;
155            return Objects.equals(plot.getArea(), that.plot.getArea())
156                    && Objects.equals(plot.getId(), that.plot.getId())
157                    && Objects.equals(plot.getOwnerAbs(), that.plot.getOwnerAbs());
158        }
159
160        @Override
161        public int hashCode() {
162            return Objects.hash(plot.getArea(), plot.getId(), plot.getOwnerAbs());
163        }
164
165    }
166
167}