diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1af7e87 --- /dev/null +++ b/pom.xml @@ -0,0 +1,127 @@ + + + 4.0.0 + + space.devport.wertik.firstdeathrewards + FirstDeathRewards + 1.0.0 + + FirstDeathRewards + + + clean install + ${project.name}-${project.version} + + + true + src/main/resources/ + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 8 + 8 + 8 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + true + + + space.devport.utils + ${project.groupId}.utils + + + + + + package + + shade + + + + + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + devport + http://play.pvpcraft.cz:8081/repository/devport/ + + + placeholderapi + http://repo.extendedclip.com/content/repositories/placeholderapi/ + + + jitpack.io + https://jitpack.io + + + + + + org.spigotmc + spigot-api + 1.16.1-R0.1-SNAPSHOT + provided + + + org.jetbrains + annotations + 15.0 + compile + + + org.projectlombok + lombok + 1.18.10 + provided + + + space.devport.utils + DevportUtils + LATEST + compile + + + com.github.MilkBowl + VaultAPI + 1.7 + provided + + + me.realized.tokenmanager + TokenManager + 3.2.5 + provided + + + me.clip + placeholderapi + 2.10.6 + provided + + + com.google.code.gson + gson + 2.8.5 + + + \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/FirstDeathLanguage.java b/src/main/java/space/devport/wertik/firstdeathrewards/FirstDeathLanguage.java new file mode 100644 index 0000000..af0502f --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/FirstDeathLanguage.java @@ -0,0 +1,19 @@ +package space.devport.wertik.firstdeathrewards; + +import space.devport.utils.text.language.LanguageDefaults; + +public class FirstDeathLanguage extends LanguageDefaults { + + @Override + public void setDefaults() { + addDefault("Commands.Invalid-Player", "&cPlayer &f%param% &cdoes not exist."); + + addDefault("Commands.Reset.Done", "&7Reset for &f%player% &7successful."); + addDefault("Commands.Reset.Done-All", "&7Reset for all players successful."); + + addDefault("Commands.Info.Message", "&8&m &7 First Death of &f%player%", "&7Available: %available%"); + addDefault("Commands.Info.You", "&6You"); + addDefault("Commands.Info.Available", "&ayes &7(means he has not died yet)"); + addDefault("Commands.Info.Not-Available", "&cno &7(means he's lame and died already)"); + } +} \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/FirstDeathPlugin.java b/src/main/java/space/devport/wertik/firstdeathrewards/FirstDeathPlugin.java new file mode 100644 index 0000000..6cb09ae --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/FirstDeathPlugin.java @@ -0,0 +1,70 @@ +package space.devport.wertik.firstdeathrewards; + +import lombok.Getter; +import space.devport.utils.DevportPlugin; +import space.devport.wertik.firstdeathrewards.commands.FirstDeathCommand; +import space.devport.wertik.firstdeathrewards.commands.subcommands.InfoSubCommand; +import space.devport.wertik.firstdeathrewards.commands.subcommands.ReloadSubCommand; +import space.devport.wertik.firstdeathrewards.commands.subcommands.ResetSubCommand; +import space.devport.wertik.firstdeathrewards.listeners.PlayerListener; +import space.devport.wertik.firstdeathrewards.system.DataManager; + +public class FirstDeathPlugin extends DevportPlugin { + + @Getter + private static FirstDeathPlugin instance; + + @Getter + private DataManager dataManager; + + @Override + public void onPluginEnable() { + instance = this; + + consoleOutput.setColors(true); + + new FirstDeathLanguage(); + + dataManager = new DataManager(); + dataManager.loadData(); + + dataManager.load(); + + dataManager.createSaveTask(); + if (getConfig().getBoolean("auto-save.enabled", false)) + dataManager.getAutoSave().start(); + + getServer().getPluginManager().registerEvents(new PlayerListener(this), this); + + addMainCommand(new FirstDeathCommand() + .addSubCommand(new InfoSubCommand()) + .addSubCommand(new ResetSubCommand()) + .addSubCommand(new ReloadSubCommand())); + } + + @Override + public void onPluginDisable() { + dataManager.save(); + } + + @Override + public void onReload() { + dataManager.loadData(); + dataManager.reloadAutoSave(); + } + + @Override + public boolean useLanguage() { + return true; + } + + @Override + public boolean useHolograms() { + return false; + } + + @Override + public boolean useMenus() { + return false; + } +} diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/commands/CommandUtils.java b/src/main/java/space/devport/wertik/firstdeathrewards/commands/CommandUtils.java new file mode 100644 index 0000000..2ecfd02 --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/commands/CommandUtils.java @@ -0,0 +1,23 @@ +package space.devport.wertik.firstdeathrewards.commands; + +import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import space.devport.wertik.firstdeathrewards.FirstDeathPlugin; + +@UtilityClass +public class CommandUtils { + + public OfflinePlayer getOfflineTarget(CommandSender sender, String name) { + OfflinePlayer offlinePlayer = Bukkit.getPlayer(name); + + if (offlinePlayer == null) { + FirstDeathPlugin.getInstance().getLanguageManager().getPrefixed("Commands.Invalid-Player") + .replace("%param%", name) + .send(sender); + return null; + } + return offlinePlayer; + } +} \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/commands/FirstDeathCommand.java b/src/main/java/space/devport/wertik/firstdeathrewards/commands/FirstDeathCommand.java new file mode 100644 index 0000000..aeed6bd --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/commands/FirstDeathCommand.java @@ -0,0 +1,27 @@ +package space.devport.wertik.firstdeathrewards.commands; + +import org.bukkit.command.CommandSender; +import space.devport.utils.commands.MainCommand; +import space.devport.utils.commands.struct.CommandResult; + +public class FirstDeathCommand extends MainCommand { + + public FirstDeathCommand() { + super("firstdeathrewards"); + } + + @Override + protected CommandResult perform(CommandSender sender, String label, String[] args) { + return CommandResult.SUCCESS; + } + + @Override + public String getDefaultUsage() { + return "/%label%"; + } + + @Override + public String getDefaultDescription() { + return "Displays this."; + } +} \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/InfoSubCommand.java b/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/InfoSubCommand.java new file mode 100644 index 0000000..16b1e2e --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/InfoSubCommand.java @@ -0,0 +1,61 @@ +package space.devport.wertik.firstdeathrewards.commands.subcommands; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import space.devport.utils.commands.SubCommand; +import space.devport.utils.commands.struct.ArgumentRange; +import space.devport.utils.commands.struct.CommandResult; +import space.devport.utils.commands.struct.Preconditions; +import space.devport.wertik.firstdeathrewards.FirstDeathPlugin; +import space.devport.wertik.firstdeathrewards.commands.CommandUtils; + +public class InfoSubCommand extends SubCommand { + + public InfoSubCommand() { + super("info"); + this.preconditions = new Preconditions() + .permissions("firstdeathrewards.info"); + } + + @Override + protected CommandResult perform(CommandSender sender, String label, String[] args) { + + boolean me = false; + + OfflinePlayer target; + if (args.length > 0) { + target = CommandUtils.getOfflineTarget(sender, args[0]); + + if (target == null) return CommandResult.FAILURE; + } else { + if (!(sender instanceof Player)) return CommandResult.NO_CONSOLE; + + target = (Player) sender; + me = true; + } + + boolean dead = FirstDeathPlugin.getInstance().getDataManager().hasFirstDeath(target); + language.get("Commands.Info.Message") + .replace("%player%", me ? language.get("Commands.Info.You").color().toString() : target.getName()) + .replace("%available%", dead ? language.get("Commands.Info.Not-Available").color().toString() : language.get("Commands.Info.Available").color().toString()) + .send(sender); + return CommandResult.SUCCESS; + } + + @Override + public @NotNull String getDefaultUsage() { + return "/%label% info (player)"; + } + + @Override + public @NotNull String getDefaultDescription() { + return "Get info about you or target."; + } + + @Override + public @NotNull ArgumentRange getRange() { + return new ArgumentRange(0, 1); + } +} \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/ReloadSubCommand.java b/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/ReloadSubCommand.java new file mode 100644 index 0000000..6e5a71f --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/ReloadSubCommand.java @@ -0,0 +1,39 @@ +package space.devport.wertik.firstdeathrewards.commands.subcommands; + +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import space.devport.utils.commands.SubCommand; +import space.devport.utils.commands.struct.ArgumentRange; +import space.devport.utils.commands.struct.CommandResult; +import space.devport.utils.commands.struct.Preconditions; +import space.devport.wertik.firstdeathrewards.FirstDeathPlugin; + +public class ReloadSubCommand extends SubCommand { + + public ReloadSubCommand() { + super("reload"); + this.preconditions = new Preconditions() + .permissions("firstdeathrewards.reload"); + } + + @Override + protected CommandResult perform(CommandSender sender, String label, String[] args) { + FirstDeathPlugin.getInstance().reload(sender); + return CommandResult.SUCCESS; + } + + @Override + public @NotNull String getDefaultUsage() { + return "/%label% reload"; + } + + @Override + public @NotNull String getDefaultDescription() { + return "Reloads the configuration."; + } + + @Override + public @NotNull ArgumentRange getRange() { + return new ArgumentRange(0); + } +} \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/ResetSubCommand.java b/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/ResetSubCommand.java new file mode 100644 index 0000000..d96eff3 --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/commands/subcommands/ResetSubCommand.java @@ -0,0 +1,59 @@ +package space.devport.wertik.firstdeathrewards.commands.subcommands; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import space.devport.utils.commands.SubCommand; +import space.devport.utils.commands.struct.ArgumentRange; +import space.devport.utils.commands.struct.CommandResult; +import space.devport.utils.commands.struct.Preconditions; +import space.devport.wertik.firstdeathrewards.FirstDeathPlugin; +import space.devport.wertik.firstdeathrewards.commands.CommandUtils; + +public class ResetSubCommand extends SubCommand { + + public ResetSubCommand() { + super("reset"); + this.preconditions = new Preconditions() + .permissions("firstdeathrewards.reset"); + } + + @Override + protected CommandResult perform(CommandSender sender, String label, String[] args) { + + OfflinePlayer target = null; + if (args.length > 0) { + if (!args[0].equalsIgnoreCase("all")) { + target = CommandUtils.getOfflineTarget(sender, args[0]); + + if (target == null) return CommandResult.FAILURE; + } + } else { + if (!(sender instanceof Player)) return CommandResult.NO_CONSOLE; + + target = (Player) sender; + } + + FirstDeathPlugin.getInstance().getDataManager().reset(target); + language.getPrefixed(target == null ? "Commands.Reset.Done-All" : "Commands.Reset.Done") + .replace("%player%", target == null ? "all" : target.getName()) + .send(sender); + return CommandResult.SUCCESS; + } + + @Override + public @NotNull String getDefaultUsage() { + return "/%label% reset (player)"; + } + + @Override + public @NotNull String getDefaultDescription() { + return "Reset all or players first death."; + } + + @Override + public @NotNull ArgumentRange getRange() { + return new ArgumentRange(0, 1); + } +} \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/listeners/PlayerListener.java b/src/main/java/space/devport/wertik/firstdeathrewards/listeners/PlayerListener.java new file mode 100644 index 0000000..1f97d61 --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/listeners/PlayerListener.java @@ -0,0 +1,30 @@ +package space.devport.wertik.firstdeathrewards.listeners; + +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import space.devport.wertik.firstdeathrewards.FirstDeathPlugin; + +@RequiredArgsConstructor +public class PlayerListener implements Listener { + + private final FirstDeathPlugin plugin; + + @EventHandler + public void onDeath(PlayerDeathEvent event) { + Player player = event.getEntity(); + + if (plugin.getConfig().getStringList("disabled-worlds").contains(player.getWorld().getName()) || + plugin.getDataManager().hasFirstDeath(player)) + return; + + plugin.getDataManager().setFirstDeath(player); + + event.setKeepInventory(plugin.getConfig().getBoolean("keep-inventory", false)); + event.setKeepLevel(plugin.getConfig().getBoolean("keep-exp", false)); + + plugin.getDataManager().getReward().give(player); + } +} \ No newline at end of file diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/system/AutoSaveTask.java b/src/main/java/space/devport/wertik/firstdeathrewards/system/AutoSaveTask.java new file mode 100644 index 0000000..fcfc4fc --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/system/AutoSaveTask.java @@ -0,0 +1,43 @@ +package space.devport.wertik.firstdeathrewards.system; + +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitTask; +import space.devport.wertik.firstdeathrewards.FirstDeathPlugin; + +@RequiredArgsConstructor +public class AutoSaveTask implements Runnable { + + private final FirstDeathPlugin plugin; + + // in ticks + private int interval; + + private boolean running = false; + + private BukkitTask task; + + public void load() { + this.interval = plugin.getConfig().getInt("auto-save.interval", 300) * 20; + } + + public void start() { + if (running) stop(); + + running = true; + task = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this, interval, interval); + } + + public void stop() { + if (!running) return; + + running = false; + task.cancel(); + task = null; + } + + @Override + public void run() { + plugin.getDataManager().save(); + } +} diff --git a/src/main/java/space/devport/wertik/firstdeathrewards/system/DataManager.java b/src/main/java/space/devport/wertik/firstdeathrewards/system/DataManager.java new file mode 100644 index 0000000..f0cd60c --- /dev/null +++ b/src/main/java/space/devport/wertik/firstdeathrewards/system/DataManager.java @@ -0,0 +1,119 @@ +package space.devport.wertik.firstdeathrewards.system; + +import com.google.common.base.Strings; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import lombok.Getter; +import org.bukkit.OfflinePlayer; +import space.devport.utils.struct.Rewards; +import space.devport.wertik.firstdeathrewards.FirstDeathPlugin; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class DataManager { + + private final FirstDeathPlugin plugin; + + private final Set cache = new HashSet<>(); + + private final Gson gson = new GsonBuilder() + // .setPrettyPrinting() + .create(); + + @Getter + private Rewards reward; + + @Getter + private AutoSaveTask autoSave; + + public DataManager() { + this.plugin = FirstDeathPlugin.getInstance(); + } + + public void setFirstDeath(OfflinePlayer player) { + cache.add(player.getUniqueId()); + } + + public void reset(OfflinePlayer... player) { + if (player.length > 0) + cache.remove(player[0].getUniqueId()); + else cache.clear(); + } + + public boolean hasFirstDeath(OfflinePlayer player) { + return cache.contains(player.getUniqueId()); + } + + public Set getCache() { + return Collections.unmodifiableSet(cache); + } + + public void loadData() { + reward = plugin.getConfiguration().getRewards("rewards"); + } + + public void createSaveTask() { + autoSave = new AutoSaveTask(plugin); + autoSave.load(); + } + + public void reloadAutoSave() { + autoSave.stop(); + + if (plugin.getConfig().getBoolean("auto-save.enabled", false)) { + autoSave.load(); + autoSave.start(); + } + } + + public void save() { + + final Set finalCache = new HashSet<>(cache); + + plugin.getConsoleOutput().debug("Saving " + cache.size() + " first death records.."); + + String output = gson.toJson(finalCache, new TypeToken>() { + }.getType()); + + plugin.getConsoleOutput().debug("JSON: " + output); + + Path path = Paths.get(plugin.getDataFolder().getPath() + "/Data.json"); + + try { + Files.write(path, output.getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void load() { + cache.clear(); + + Path path = Paths.get(plugin.getDataFolder().getPath() + "/Data.json"); + + if (!Files.exists(path)) return; + + String input; + try { + input = String.join("", Files.readAllLines(path)); + } catch (IOException e) { + e.printStackTrace(); + return; + } + + if (Strings.isNullOrEmpty(input)) return; + + cache.addAll(gson.fromJson(input, new TypeToken>() { + }.getType())); + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..99ce42f --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,28 @@ +# Configuration for ${project.name} +# +# Version: $[project.version} + +plugin-prefix: '&cFirst Death &8>> ' +debug-enabled: true + +# Automatic saving +auto-save: + enabled: true + # in seconds + interval: 300 + +# Where the plugin won't work. +disabled-worlds: + - event_shit_world + +# Keep inventory and exp on fist death? +keep-inventory: true +keep-exp: true + +# Rewards given to player upon death +rewards: + inform: + - "&7You died. You're incompetent. Basically useless when the only purpose was to &2&osurvive&7... " + - "&7But we'll give you one more chance... just so you know we're the &e&onice guys &7here." + - "&7Now live and &c&odon't die &7again you &e&odipshit." + # commands, inform, broadcast, items, tokens, money \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..8d2341b --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,9 @@ +name: ${project.name} +version: ${project.version} +main: ${project.groupId}.FirstDeathPlugin +softdepend: [Vault, TokenManager, PlaceholderAPI] +authors: [Wertik1206] +commands: + firstdeathrewards: + description: Main plugin command + aliases: [fd, fdr, firstdeath] \ No newline at end of file