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.TranslatableCaption; 028import com.plotsquared.core.inject.factory.PlayerBackupProfileFactory; 029import com.plotsquared.core.player.PlotPlayer; 030import com.plotsquared.core.plot.Plot; 031import com.plotsquared.core.util.task.TaskManager; 032import net.kyori.adventure.text.Component; 033import net.kyori.adventure.text.minimessage.tag.Tag; 034import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 035import org.checkerframework.checker.nullness.qual.NonNull; 036import org.checkerframework.checker.nullness.qual.Nullable; 037 038import java.nio.file.Files; 039import java.nio.file.Path; 040import java.util.Objects; 041import java.util.concurrent.ExecutionException; 042import java.util.concurrent.TimeUnit; 043 044/** 045 * {@inheritDoc} 046 */ 047@Singleton 048public class SimpleBackupManager implements BackupManager { 049 050 private final Path backupPath; 051 private final boolean automaticBackup; 052 private final int backupLimit; 053 private final Cache<PlotCacheKey, BackupProfile> backupProfileCache = CacheBuilder.newBuilder() 054 .expireAfterAccess(3, TimeUnit.MINUTES).build(); 055 private final PlayerBackupProfileFactory playerBackupProfileFactory; 056 057 @Inject 058 public SimpleBackupManager(final @NonNull PlayerBackupProfileFactory playerBackupProfileFactory) throws Exception { 059 this.playerBackupProfileFactory = playerBackupProfileFactory; 060 this.backupPath = Objects.requireNonNull(PlotSquared.platform()).getDirectory().toPath().resolve("backups"); 061 if (!Files.exists(backupPath)) { 062 Files.createDirectory(backupPath); 063 } 064 this.automaticBackup = Settings.Backup.AUTOMATIC_BACKUPS; 065 this.backupLimit = Settings.Backup.BACKUP_LIMIT; 066 } 067 068 public SimpleBackupManager( 069 final Path backupPath, final boolean automaticBackup, 070 final int backupLimit, final PlayerBackupProfileFactory playerBackupProfileFactory 071 ) { 072 this.backupPath = backupPath; 073 this.automaticBackup = automaticBackup; 074 this.backupLimit = backupLimit; 075 this.playerBackupProfileFactory = playerBackupProfileFactory; 076 } 077 078 @Override 079 public @NonNull BackupProfile getProfile(final @NonNull Plot plot) { 080 if (plot.hasOwner()) { 081 try { 082 return backupProfileCache.get( 083 new PlotCacheKey(plot), 084 () -> this.playerBackupProfileFactory.create(plot.getOwnerAbs(), plot) 085 ); 086 } catch (ExecutionException e) { 087 final BackupProfile profile = this.playerBackupProfileFactory.create(plot.getOwnerAbs(), plot); 088 this.backupProfileCache.put(new PlotCacheKey(plot), profile); 089 return profile; 090 } 091 } 092 return new NullBackupProfile(); 093 } 094 095 @Override 096 public void automaticBackup(@Nullable PlotPlayer<?> player, final @NonNull Plot plot, @NonNull Runnable whenDone) { 097 final BackupProfile profile; 098 if (!this.shouldAutomaticallyBackup() || (profile = getProfile(plot)) instanceof NullBackupProfile) { 099 whenDone.run(); 100 } else { 101 if (player != null) { 102 player.sendMessage( 103 TranslatableCaption.of("backups.backup_automatic_started"), 104 TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) 105 ); 106 } 107 profile.createBackup().whenComplete((backup, throwable) -> { 108 if (throwable != null) { 109 if (player != null) { 110 player.sendMessage( 111 TranslatableCaption.of("backups.backup_automatic_failure"), 112 TagResolver.resolver("reason", Tag.inserting(Component.text(throwable.getMessage()))) 113 ); 114 } 115 throwable.printStackTrace(); 116 } else { 117 if (player != null) { 118 player.sendMessage(TranslatableCaption.of("backups.backup_automatic_finished")); 119 TaskManager.runTaskAsync(whenDone); 120 } 121 } 122 }); 123 } 124 } 125 126 @Override 127 public boolean shouldAutomaticallyBackup() { 128 return this.automaticBackup; 129 } 130 131 public Path getBackupPath() { 132 return this.backupPath; 133 } 134 135 public int getBackupLimit() { 136 return this.backupLimit; 137 } 138 139 private record PlotCacheKey( 140 Plot plot 141 ) { 142 143 @Override 144 public boolean equals(final Object o) { 145 if (this == o) { 146 return true; 147 } 148 if (o == null || getClass() != o.getClass()) { 149 return false; 150 } 151 final PlotCacheKey that = (PlotCacheKey) o; 152 return Objects.equals(plot.getArea(), that.plot.getArea()) 153 && Objects.equals(plot.getId(), that.plot.getId()) 154 && Objects.equals(plot.getOwnerAbs(), that.plot.getOwnerAbs()); 155 } 156 157 @Override 158 public int hashCode() { 159 return Objects.hash(plot.getArea(), plot.getId(), plot.getOwnerAbs()); 160 } 161 162 } 163 164}