package net.knarcraft.paidsigns.manager; import net.knarcraft.paidsigns.PaidSigns; import net.knarcraft.paidsigns.container.TrackedSign; import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage; import net.knarcraft.paidsigns.utility.SignHelper; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.logging.Level; /** * A manager for keeping track of plugin-signs created by players */ public final class TrackedSignManager { private static Map trackedSigns = new HashMap<>(); private static final File signsFile = new File(PaidSigns.getInstance().getDataFolder(), "data.yml"); private TrackedSignManager() { } /** * Adds a tracked sign to the manager * * @param signLocation

The location the sign was created at

* @param playerId

The unique id of the player that created the sign

* @throws IOException

If unable to save the tracked signs

*/ public static void addTrackedSign(Location signLocation, UUID playerId, double cost) throws IOException { trackedSigns.put(signLocation, new TrackedSign(playerId, cost)); saveTrackedSigns(); } /** * Removes a tracked sign from the manager * * @param signLocation

The location the sign was removed from

* @param refund

Whether to perform a refund after un-tracking the sign

* @param forceRefund

Whether to force a refund, even if refunding is disabled

* @throws IOException

If unable to save the tracked signs

*/ public static void removeTrackedSign(Location signLocation, boolean refund, boolean forceRefund) throws IOException { if (!trackedSigns.containsKey(signLocation)) { return; } TrackedSign trackedSign = trackedSigns.get(signLocation); trackedSigns.remove(signLocation); saveTrackedSigns(); refund(trackedSign, refund, forceRefund); } /** * Loads all tracked signs from the data file */ public static void loadTrackedSigns() { YamlConfiguration configuration = YamlConfiguration.loadConfiguration(signsFile); ConfigurationSection signSection = configuration.getConfigurationSection("trackedSigns"); trackedSigns = new HashMap<>(); if (signSection == null) { PaidSigns.getInstance().getLogger().log(Level.INFO, "Tracked signs section not found in data.yml"); return; } for (String key : signSection.getKeys(false)) { loadSign(signSection, key); } //Save tracked signs in case some were invalidated after loading try { TrackedSignManager.saveTrackedSigns(); } catch (IOException e) { PaidSigns.getInstance().getLogger().log(Level.WARNING, "Unable to save tracked signs"); } } /** * Loads a sign from the save file * * @param signSection

The configuration section containing signs

* @param key

The sign key which is also the sign's location

*/ private static void loadSign(ConfigurationSection signSection, String key) { String[] locationParts = key.split(","); Location signLocation; try { signLocation = new Location(Bukkit.getWorld(UUID.fromString(locationParts[0])), Double.parseDouble(locationParts[1]), Double.parseDouble(locationParts[2]), Double.parseDouble(locationParts[3])); } catch (NumberFormatException exception) { PaidSigns.getInstance().getLogger().log(Level.SEVERE, "Unable to load tracked sign " + key + ": " + exception.getMessage()); return; } double cost = signSection.getDouble(key + ".cost"); UUID playerId = UUID.fromString(Objects.requireNonNull(signSection.getString(key + ".playerId"))); TrackedSign trackedSign = new TrackedSign(playerId, cost); //Prevent destroyed signs from being tracked indefinitely if (!SignHelper.isSign(signLocation.getBlock())) { Bukkit.getScheduler().scheduleSyncDelayedTask(PaidSigns.getInstance(), () -> { PaidSigns.getInstance().getLogger().log(Level.WARNING, "The sign at " + signLocation + " no longer exists. Removing from sign tracker. Refunding the player."); refund(trackedSign, true, false); }, 100); return; } trackedSigns.put(signLocation, trackedSign); } /** * Saves the managed tracked signs to the data file * * @throws IOException

If unable to write to the data file

*/ public static void saveTrackedSigns() throws IOException { YamlConfiguration configuration = YamlConfiguration.loadConfiguration(signsFile); ConfigurationSection signSection = configuration.createSection("trackedSigns"); for (Location signLocation : trackedSigns.keySet()) { TrackedSign sign = trackedSigns.get(signLocation); String locationString = Objects.requireNonNull(signLocation.getWorld()).getUID() + "," + signLocation.getBlockX() + "," + signLocation.getBlockY() + "," + signLocation.getBlockZ(); signSection.set(locationString + ".cost", sign.cost()); signSection.set(locationString + ".playerId", sign.playerId().toString()); } configuration.save(signsFile); } /** * Refunds the player for the sign if refunds are enabled and refund is set to true * * @param trackedSign

The tracked sign to refund for

* @param refund

Whether to actually refund

* @param forceRefund

Whether to force a refund, even if refunding is disabled

*/ private static void refund(TrackedSign trackedSign, boolean refund, boolean forceRefund) { if ((!PaidSigns.getInstance().areRefundsEnabled() || !refund) && !forceRefund) { return; } OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(trackedSign.playerId()); double refundSum; if (forceRefund) { //In the case where a refund is forced, the normal refund rate should not apply refundSum = trackedSign.cost(); } else { refundSum = trackedSign.cost() / 100 * PaidSigns.getInstance().getRefundPercentage(); } EconomyManager.deposit(offlinePlayer, refundSum); if (offlinePlayer instanceof Player player) { PaidSigns.getStringFormatter().displaySuccessMessage(player, PaidSigns.getStringFormatter().replacePlaceholder(PaidSignsTranslatableMessage.SUCCESS_REFUNDED, "{cost}", EconomyManager.format(refundSum))); } } }