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