diff --git a/CHANGELOG.md b/CHANGELOG.md index 7738de92..c6aa4d76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +v1.6.0 + ++ SQL Support ++ Reusable job sets that allow for easier job creation ++ Add option to change location of currency symbol (eg. $5 or 5$) ++ Add `growthTrait` node to `break` nodes to support crops (see the farmer job) ++ Add `farmer` to the default jobs ++ Add `/job set ` command to set another players job ++ Virtual account support ++ CHANGE: Updated format of job info command ++ CHANGE: Commands now properly return as successful or unsuccessful ++ CHANGE: Baltop command now only shows a maximum of 10 balances ++ CHANGE: Added "/money" alias to balance command ++ CHANGE: Pay command no longer works from command blocks or console (Use adminpay) ++ CHANGE: In case of duplicate rewards in a job (across multiple sets) the one giving more exp is chosen rather than the last match ++ CHANGE: No gains from breaking a block placed by any player UNLESS the block is indicating to have a `growTrait` + v1.5.3 + FIX: NullPointerException no longer occurs when reloading configuration files and not having jobs enabled diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 052d3911..e3e2f13c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,16 +3,12 @@ Thanks for your interest in contributing to Total Economy. There are a few guide ### Contributing Issues * Be as descriptive as possible -* Include a complete error log +* Include a complete error log if applicable * Include pictures if possible ### Contributing Code * Fork the repository -* Create a new branch from ‘develop'. Use slash notation (e.g. feature/feature-name) - * Types: - * feature - * fix - * update +* Create a new branch from ‘develop'. Use slash notation (e.g. issue/issue#-short-issue-summary, issue/137-nfe-on-get-balance) * Follow the same format/style as the rest of the code * Add comments for new functions: ``` java @@ -24,7 +20,7 @@ Thanks for your interest in contributing to Total Economy. There are a few guide * @return int player's balance */ ``` -* Test the change/addition and make sure nothing was accidently broken +* Test the change/addition and make sure nothing was accidentally broken * Make sure your commit message clearly describes the change/addition and includes the issue number if one exists * Submit a pull request diff --git a/README.md b/README.md index 60ff0007..57a56477 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # TotalEconomy + All in one economy plugin for Minecraft and Sponge. -- -##Commands + +## Commands ``` /pay [player] [amount] - Pay another player /balance - Display your balance (/bal) @@ -17,7 +18,7 @@ All in one economy plugin for Minecraft and Sponge. /balancetop - Display a paginated list of top balances (/baltop) ``` -##Permissions +## Permissions ``` totaleconomy.command.pay totaleconomy.command.balance diff --git a/build.gradle b/build.gradle index de74cf29..e117ead4 100644 --- a/build.gradle +++ b/build.gradle @@ -9,5 +9,10 @@ repositories { apply plugin: 'java' dependencies { - compile 'org.spongepowered:spongeapi:5.1.0-SNAPSHOT' + compile 'org.spongepowered:spongeapi:6.0.0' } + +jar { + baseName="TotalEconomy" + version="1.6.0" +} \ No newline at end of file diff --git a/src/main/java/com/erigitic/commands/AdminPayCommand.java b/src/main/java/com/erigitic/commands/AdminPayCommand.java index 57e62466..12a2528d 100644 --- a/src/main/java/com/erigitic/commands/AdminPayCommand.java +++ b/src/main/java/com/erigitic/commands/AdminPayCommand.java @@ -35,6 +35,7 @@ import org.spongepowered.api.command.args.CommandContext; import org.spongepowered.api.command.spec.CommandExecutor; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.User; import org.spongepowered.api.event.cause.Cause; import org.spongepowered.api.event.cause.NamedCause; import org.spongepowered.api.service.economy.Currency; @@ -44,6 +45,8 @@ import org.spongepowered.api.text.format.TextColors; import java.math.BigDecimal; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class AdminPayCommand implements CommandExecutor { private Logger logger; @@ -57,40 +60,50 @@ public AdminPayCommand(TotalEconomy totalEconomy) { accountManager = totalEconomy.getAccountManager(); - defaultCurrency = accountManager.getDefaultCurrency(); + defaultCurrency = totalEconomy.getDefaultCurrency(); } @Override public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { - String strAmount = args.getOne("amount").get(); - Player recipient = args.getOne("player").get(); + String strAmount = (String) args.getOne("amount").get(); + User recipient = args.getOne("player").get(); - if (TotalEconomy.isNumeric(strAmount)) { + + Pattern amountPattern = Pattern.compile("^[+-]?(\\d*\\.)?\\d+$"); + Matcher m = amountPattern.matcher(strAmount); + + if (m.matches()) { BigDecimal amount = new BigDecimal((String) args.getOne("amount").get()).setScale(2, BigDecimal.ROUND_DOWN); TEAccount recipientAccount = (TEAccount) accountManager.getOrCreateAccount(recipient.getUniqueId()).get(); Text amountText = Text.of(defaultCurrency.format(amount).toPlain().replace("-", "")); - TransactionResult transactionResult = recipientAccount.deposit(accountManager.getDefaultCurrency(), amount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + TransactionResult transactionResult = recipientAccount.deposit(totalEconomy.getDefaultCurrency(), amount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); if (transactionResult.getResult() == ResultType.SUCCESS) { if (!strAmount.contains("-")) { src.sendMessage(Text.of(TextColors.GRAY, "You have sent ", TextColors.GOLD, amountText, TextColors.GRAY, " to ", TextColors.GOLD, recipient.getName(), TextColors.GRAY, ".")); - recipient.sendMessage(Text.of(TextColors.GRAY, "You have received ", TextColors.GOLD, amountText, - TextColors.GRAY, " from ", TextColors.GOLD, src.getName(), TextColors.GRAY, ".")); + if (recipient.isOnline()) { + recipient.getPlayer().get().sendMessage(Text.of(TextColors.GRAY, "You have received ", TextColors.GOLD, amountText, + TextColors.GRAY, " from ", TextColors.GOLD, src.getName(), TextColors.GRAY, ".")); + } } else { src.sendMessage(Text.of(TextColors.GRAY, "You have removed ", TextColors.GOLD, amountText, TextColors.GRAY, " from ", TextColors.GOLD, recipient.getName(), "'s", TextColors.GRAY, " account.")); - recipient.sendMessage(Text.of(TextColors.GOLD, amountText, TextColors.GRAY, " has been removed from your account by ", - TextColors.GOLD, src.getName(), TextColors.GRAY, ".")); + if (recipient.isOnline()) { + recipient.getPlayer().get().sendMessage(Text.of(TextColors.GOLD, amountText, TextColors.GRAY, " has been removed from your account by ", + TextColors.GOLD, src.getName(), TextColors.GRAY, ".")); + } } + + return CommandResult.success(); } } else { - src.sendMessage(Text.of(TextColors.RED, "The amount must be numeric.")); + throw new CommandException(Text.of("Invalid amount! Must be a number!")); } - return CommandResult.success(); + return CommandResult.empty(); } } diff --git a/src/main/java/com/erigitic/commands/BalanceCommand.java b/src/main/java/com/erigitic/commands/BalanceCommand.java index 00d66887..9c2882dc 100644 --- a/src/main/java/com/erigitic/commands/BalanceCommand.java +++ b/src/main/java/com/erigitic/commands/BalanceCommand.java @@ -56,12 +56,14 @@ public CommandResult execute(CommandSource src, CommandContext args) throws Comm if (src instanceof Player) { Player sender = (Player) src; TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(sender.getUniqueId()).get(); - Currency defaultCurrency = accountManager.getDefaultCurrency(); + Currency defaultCurrency = totalEconomy.getDefaultCurrency(); Text playerBalance = defaultCurrency.format(playerAccount.getBalance(defaultCurrency)); sender.sendMessage(Text.of(TextColors.GRAY, "Balance: ", TextColors.GOLD, playerBalance)); + + return CommandResult.success(); } - return CommandResult.success(); + return CommandResult.empty(); } } diff --git a/src/main/java/com/erigitic/commands/BalanceTopCommand.java b/src/main/java/com/erigitic/commands/BalanceTopCommand.java index ba421dbf..771d12b8 100644 --- a/src/main/java/com/erigitic/commands/BalanceTopCommand.java +++ b/src/main/java/com/erigitic/commands/BalanceTopCommand.java @@ -69,24 +69,30 @@ public CommandResult execute(CommandSource src, CommandContext args) throws Comm ConfigurationNode accountNode = accountManager.getAccountConfig(); List accountBalances = new ArrayList<>(); Map accountBalancesMap = new HashMap<>(); - Currency defaultCurrency = accountManager.getDefaultCurrency(); + Currency defaultCurrency = totalEconomy.getDefaultCurrency(); - // TODO: Add customization to this (amount of accounts to show). accountNode.getChildrenMap().keySet().forEach(accountUUID -> { - TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(UUID.fromString(accountUUID.toString())).get(); + UUID uuid; + + // Check if the account is virtual or not. If virtual, skip the rest of the execution and move on to next account. + try { + uuid = UUID.fromString(accountUUID.toString()); + } catch (IllegalArgumentException e) { + return; + } + + TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(uuid).get(); Text playerName = playerAccount.getDisplayName(); accountBalancesMap.put(playerName.toPlain(), playerAccount.getBalance(defaultCurrency)); }); - List> unsortedList = new LinkedList<>(accountBalancesMap.entrySet()); - unsortedList.sort((Map.Entry o1, Map.Entry o2) -> (o1.getValue()).compareTo(o2.getValue())); - - Collections.reverse(unsortedList); - - unsortedList.forEach(entry -> accountBalances.add(Text.of(TextColors.GRAY, entry.getKey(), ": ", TextColors.GOLD, defaultCurrency.format(entry.getValue()).toPlain()))); + accountBalancesMap.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(10) + .forEach(entry -> accountBalances.add(Text.of(TextColors.GRAY, entry.getKey(), ": ", TextColors.GOLD, defaultCurrency.format(entry.getValue()).toPlain()))); - builder.title(Text.of(TextColors.GOLD, "Top Balances")) + builder.title(Text.of(TextColors.GOLD, "Top 10 Balances")) .contents(accountBalances) .sendTo(src); diff --git a/src/main/java/com/erigitic/commands/JobCommand.java b/src/main/java/com/erigitic/commands/JobCommand.java index 70f70c1d..4781c3e4 100644 --- a/src/main/java/com/erigitic/commands/JobCommand.java +++ b/src/main/java/com/erigitic/commands/JobCommand.java @@ -25,46 +25,255 @@ package com.erigitic.commands; -import com.erigitic.config.AccountManager; -import com.erigitic.jobs.TEJobs; +import com.erigitic.config.TECurrency; +import com.erigitic.jobs.*; import com.erigitic.main.TotalEconomy; +import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandException; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.args.CommandContext; +import org.spongepowered.api.command.args.GenericArguments; import org.spongepowered.api.command.spec.CommandExecutor; +import org.spongepowered.api.command.spec.CommandSpec; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.User; +import org.spongepowered.api.service.economy.Currency; +import org.spongepowered.api.service.pagination.PaginationList; +import org.spongepowered.api.service.pagination.PaginationService; import org.spongepowered.api.text.Text; import org.spongepowered.api.text.format.TextColors; +import org.spongepowered.api.text.format.TextStyles; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; public class JobCommand implements CommandExecutor { - private AccountManager accountManager; - private TEJobs teJobs; + + private TotalEconomy totalEconomy; public JobCommand(TotalEconomy totalEconomy) { - accountManager = totalEconomy.getAccountManager(); - teJobs = totalEconomy.getTEJobs(); + this.totalEconomy = totalEconomy; + } + + public static CommandSpec commandSpec(TotalEconomy totalEconomy) { + return CommandSpec.builder() + .child(Set.commandSpec(totalEconomy), "set", "s") + .child(Toggle.commandSpec(totalEconomy), "toggle", "t") + .child(Info.commandSpec(totalEconomy), "info", "i") + .child(Reload.commandSpec(totalEconomy), "reload") + .description(Text.of("Display list of jobs.")) + .permission("totaleconomy.command.job") + .arguments(GenericArguments.none()) + .executor(new JobCommand(totalEconomy)) + .build(); } @Override public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { if (src instanceof Player) { - Player sender = ((Player) src).getPlayer().get(); + Player player = ((Player) src).getPlayer().get(); + String jobName = totalEconomy.getTEJobManager().getPlayerJob(player); - if (args.getOne("jobName").isPresent()) { - String jobName = args.getOne("jobName").get().toString().toLowerCase(); + player.sendMessage(Text.of(TextColors.GRAY, "Current Job: ", TextColors.GOLD, totalEconomy.getTEJobManager().titleize(jobName))); + player.sendMessage(Text.of(TextColors.GRAY, totalEconomy.getTEJobManager().titleize(jobName), + " Level: ", TextColors.GOLD, totalEconomy.getTEJobManager().getJobLevel(jobName, player))); + player.sendMessage(Text.of(TextColors.GRAY, totalEconomy.getTEJobManager().titleize(jobName), + " Exp: ", TextColors.GOLD, totalEconomy.getTEJobManager().getJobExp(jobName, player), "/", totalEconomy.getTEJobManager().getExpToLevel(player), " exp\n")); + player.sendMessage(Text.of(TextColors.GRAY, "Available Jobs: ", TextColors.GOLD, totalEconomy.getTEJobManager().getJobList())); - teJobs.setJob(sender, jobName); - } else { - String jobName = teJobs.getPlayerJob(sender); + return CommandResult.success(); + } else { + throw new CommandException(Text.of("You can't have a job!")); + } + } + + public static class Set implements CommandExecutor { + + private TotalEconomy totalEconomy; + + public Set(TotalEconomy totalEconomy) { + this.totalEconomy = totalEconomy; + } + + public static CommandSpec commandSpec(TotalEconomy totalEconomy) { + return CommandSpec.builder() + .description(Text.of("Set your job")) + .permission(TEPermissions.JOB_SET) + .executor(new Set(totalEconomy)) + .arguments( + GenericArguments.string(Text.of("jobName")), + GenericArguments.requiringPermission( + GenericArguments.userOrSource(Text.of("user")), + TEPermissions.JOB_SET_OTHERS + ) + ) + .build(); + } + + @Override + public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { + // TODO: Do checks here, in case we or other plugins want to bypass them in the future + + String jobName = args.getOne("jobName").get().toString().toLowerCase(); + User user = args.getOne("user").get(); + + Optional optJob = totalEconomy.getTEJobManager().getJob(jobName, false); + if (!optJob.isPresent()) throw new CommandException(Text.of("Job " + jobName + " does not exist!")); + + TEJob job = optJob.get(); + + if (job.getRequirement().isPresent()) { + JobBasedRequirement req = job.getRequirement().get(); + + if (req.permissionNeeded() != null && !user.hasPermission(req.permissionNeeded())) { + throw new CommandException(Text.of("Not permitted to join job \"", TextColors.GOLD, jobName, TextColors.RED, "\"")); + } + + if (req.jobNeeded() != null && req.jobLevelNeeded() > totalEconomy.getTEJobManager().getJobLevel(req.jobNeeded().toLowerCase(), user)) { + throw new CommandException(Text.of("Insufficient level! Level ", + TextColors.GOLD, req.jobLevelNeeded(), TextColors.RED," as a ", + TextColors.GOLD, req.jobNeeded(), TextColors.RED, " required!")); + } + } + + if (!totalEconomy.getTEJobManager().setJob(user, jobName)) { + throw new CommandException(Text.of("Failed to set job. Contact your administrator.")); + } else if (user.getPlayer().isPresent()) { + user.getPlayer().get().sendMessage(Text.of(TextColors.GRAY, "Job set to: ", TextColors.GOLD, totalEconomy.getTEJobManager().titleize(jobName))); + } + + // Only send additional feedback if CommandSource isn't the target. + if (!(src instanceof User) || !((User) src).getUniqueId().equals(user.getUniqueId())) { + src.sendMessage(Text.of(TextColors.GREEN, "Job set.")); + } + + return CommandResult.success(); + } + } + + public static class Info implements CommandExecutor { + + private TotalEconomy totalEconomy; + + public Info(TotalEconomy totalEconomy) { + this.totalEconomy = totalEconomy; + } + + public static CommandSpec commandSpec(TotalEconomy totalEconomy) { + return CommandSpec.builder() + .description(Text.of("Prints out a list of items that reward exp and money for the current job")) + .permission("totaleconomy.command.job.info") + .executor(new Info(totalEconomy)) + .arguments(GenericArguments.optional(GenericArguments.string(Text.of("jobName")))) + .build(); + } + + // Setup pagination + private PaginationService paginationService = Sponge.getServiceManager().provideUnchecked(PaginationService.class); + private PaginationList.Builder pageBuilder = paginationService.builder(); + + @Override + public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { + TEJobManager teJobManager = totalEconomy.getTEJobManager(); - sender.sendMessage(Text.of(TextColors.GRAY, "Your current job is: ", TextColors.GOLD, jobName)); - sender.sendMessage(Text.of(TextColors.GRAY, jobName, " Level: ", TextColors.GOLD, teJobs.getJobLevel(jobName, sender))); - sender.sendMessage(Text.of(TextColors.GRAY, jobName, " Exp: ", TextColors.GOLD, teJobs.getJobExp(jobName, sender), "/", teJobs.getExpToLevel(sender), "\n")); - sender.sendMessage(Text.of(TextColors.GRAY, "Available Jobs: ", TextColors.GOLD, teJobs.getJobList())); + Optional optJobName = args.getOne("jobName"); + Optional optJob = Optional.empty(); + + if (!optJobName.isPresent() && (src instanceof Player)) { + optJob = teJobManager.getJob(teJobManager.getPlayerJob((Player) src), true); + } + + if (optJobName.isPresent()) { + optJob = teJobManager.getJob(optJobName.get(), false); + } + + if (!optJob.isPresent()) { + throw new CommandException(Text.of(TextColors.RED, "Unknown job: \"" + optJobName.orElse("") + "\"")); + } + + List lines = new ArrayList(); + + for (String s : optJob.get().getSets()) { + Optional optSet = teJobManager.getJobSet(s); + + if (optSet.isPresent()) { + TEJobSet jobSet = optSet.get(); + Currency defaultCurrency = totalEconomy.getDefaultCurrency(); + + for (TEActionReward actionReward : jobSet.getActionRewards()) { + lines.add(Text.of(TextColors.GOLD, "[", teJobManager.titleize(actionReward.getAction()), "] ", TextColors.GRAY, actionReward.getTargetID(), TextColors.GOLD, " (", actionReward.getExpReward(), " EXP) (", defaultCurrency.format(actionReward.getMoneyReward()), ")")); + } + } + } + + pageBuilder.reset() + .header(Text.of(TextColors.GRAY, "Job information for ", TextColors.GOLD, optJobName.orElseGet(() -> teJobManager.getPlayerJob(((Player) src))),"\n")) + .contents(lines.toArray(new Text[lines.size()])) + .sendTo(src); + + return CommandResult.success(); + } + } + + public static class Reload implements CommandExecutor { + + private TotalEconomy totalEconomy; + + public Reload(TotalEconomy totalEconomy) { + this.totalEconomy = totalEconomy; + } + + public static CommandSpec commandSpec(TotalEconomy totalEconomy) { + return CommandSpec.builder() + .description(Text.of("Reloads sets and jobs")) + .permission("totaleconomy.command.job.reload") + .executor(new Reload(totalEconomy)) + .arguments(GenericArguments.none()) + .build(); + } + + @Override + public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { + if (totalEconomy.getTEJobManager().reloadJobsAndSets()) { + src.sendMessage(Text.of(TextColors.GRAY, "[TE] Sets and jobs reloaded.")); + } else { + throw new CommandException(Text.of(TextColors.RED, "[TE] Failed to reload sets and/or jobs!")); } + + return CommandResult.success(); + } + } + + public static class Toggle implements CommandExecutor { + + private TotalEconomy totalEconomy; + + public Toggle(TotalEconomy totalEconomy) { + this.totalEconomy = totalEconomy; } - return CommandResult.success(); + public static CommandSpec commandSpec(TotalEconomy totalEconomy) { + return CommandSpec.builder() + .description(Text.of("Toggle job notifications on/off")) + .permission("totaleconomy.command.job.toggle") + .executor(new Toggle(totalEconomy)) + .build(); + } + + @Override + public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { + if (src instanceof Player) { + Player sender = ((Player) src).getPlayer().get(); + + totalEconomy.getAccountManager().toggleNotifications(sender); + + return CommandResult.success(); + } + + return CommandResult.empty(); + } } } \ No newline at end of file diff --git a/src/main/java/com/erigitic/commands/JobInfoCommand.java b/src/main/java/com/erigitic/commands/JobInfoCommand.java deleted file mode 100644 index 55cc9828..00000000 --- a/src/main/java/com/erigitic/commands/JobInfoCommand.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of Total Economy, licensed under the MIT License (MIT). - * - * Copyright (c) Eric Grandt - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.erigitic.commands; - -import com.erigitic.config.AccountManager; -import com.erigitic.jobs.TEJobs; -import com.erigitic.main.TotalEconomy; -import ninja.leaping.configurate.ConfigurationNode; -import org.apache.commons.lang3.text.WordUtils; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.CommandException; -import org.spongepowered.api.command.CommandResult; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.command.args.CommandContext; -import org.spongepowered.api.command.spec.CommandExecutor; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.service.pagination.PaginationList; -import org.spongepowered.api.service.pagination.PaginationService; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.text.format.TextStyles; - -import java.util.ArrayList; -import java.util.List; - -public class JobInfoCommand implements CommandExecutor { - private TEJobs teJobs; - private AccountManager accountManager; - - private ConfigurationNode jobsConfig; - - // Setup pagination - private PaginationService paginationService = Sponge.getServiceManager().provideUnchecked(PaginationService.class); - private PaginationList.Builder builder = paginationService.builder(); - - public JobInfoCommand(TotalEconomy totalEconomy) { - teJobs = totalEconomy.getTEJobs(); - accountManager = totalEconomy.getAccountManager(); - - jobsConfig = teJobs.getJobsConfig(); - } - - @Override - public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { - if (src instanceof Player) { - Player sender = ((Player) src).getPlayer().get(); - String jobName = teJobs.getPlayerJob(sender); - List jobValues = new ArrayList<>(); - - // TODO: There is probably a much better way of doing this. - boolean hasBreakNode = (jobsConfig.getNode(jobName, "break").getValue() != null); - boolean hasPlaceNode = (jobsConfig.getNode(jobName, "place").getValue() != null); - boolean hasCatchNode = (jobsConfig.getNode(jobName, "catch").getValue() != null); - boolean hasKillNode = (jobsConfig.getNode(jobName, "kill").getValue() != null); - - // TODO: Same with this, probably a much better way of doing this. - if (hasBreakNode) { jobValues.addAll(getJobValues(jobName, "break", "Breakables")); } - if (hasPlaceNode) { jobValues.addAll(getJobValues(jobName, "place", "Placeables")); } - if (hasCatchNode) { jobValues.addAll(getJobValues(jobName, "catch", "Catchables")); } - if (hasKillNode) { jobValues.addAll(getJobValues(jobName, "kill", "Killables")); } - - printNodeChildren(sender, jobValues); - } - - return CommandResult.success(); - } - - /** - * Gets a list of items that reward the player for doing a certain job - * - * @param jobName players current job - * @param nodeName node type (break, catch, etc.) - * @param title - * @return List formatted text containing job values - */ - private List getJobValues(String jobName, String nodeName, String title) { - List jobValues = new ArrayList<>(); - - jobsConfig.getNode(jobName, nodeName).getChildrenMap().keySet().forEach(value -> { - if (value instanceof String) { - String expReward = jobsConfig.getNode(jobName, nodeName, value, "expreward").getString(); - String moneyReward = jobsConfig.getNode(jobName, nodeName, value, "pay").getString(); - String valueFormatted; - - if (((String) value).contains(":")) - value = ((String) value).split(":")[1]; - - valueFormatted = WordUtils.capitalize(((String) value).replaceAll("_", " ")); - - jobValues.add(Text.of(TextColors.LIGHT_PURPLE, WordUtils.capitalize(nodeName + ": "), TextColors.GRAY, - valueFormatted, " | ", TextColors.GREEN, expReward, " exp", TextColors.GRAY, " | ", TextColors.GOLD, - accountManager.getDefaultCurrency().getSymbol(), moneyReward)); - } - }); - - return jobValues; - } - - /** - * Print the job values in a paginated list - * - * @param sender player who sent the command - * @param jobValues list of the formatted job values - */ - private void printNodeChildren(Player sender, List jobValues) { - builder.reset().title(Text.of(TextColors.GOLD, TextStyles.BOLD, "Job Information")) - .contents(jobValues) - .padding(Text.of(TextColors.GRAY, "-")) - .sendTo(sender); - } -} diff --git a/src/main/java/com/erigitic/commands/PayCommand.java b/src/main/java/com/erigitic/commands/PayCommand.java index c07630c6..1523fa4d 100644 --- a/src/main/java/com/erigitic/commands/PayCommand.java +++ b/src/main/java/com/erigitic/commands/PayCommand.java @@ -47,6 +47,8 @@ import org.spongepowered.api.text.format.TextColors; import java.math.BigDecimal; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class PayCommand implements CommandExecutor { private Logger logger; @@ -60,70 +62,48 @@ public PayCommand(TotalEconomy totalEconomy) { accountManager = totalEconomy.getAccountManager(); - defaultCurrency = accountManager.getDefaultCurrency(); + defaultCurrency = totalEconomy.getDefaultCurrency(); } @Override public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { - //TODO: This makes my eyes hurt. Come back later to possibly make it look pretty. + String strAmount = (String) args.getOne("amount").get(); + Player recipient = (Player) args.getOne("player").get(); + if (src instanceof Player) { - Player sender = ((Player) src).getPlayer().get(); - Object playerArg = args.getOne("player").get(); - String strAmount = (String) args.getOne("amount").get(); - - if (totalEconomy.isNumeric(strAmount)) { - // Check for a negative number - if (!strAmount.contains("-")) { - if (playerArg instanceof Player) { - Player recipient = (Player) playerArg; - BigDecimal amount = new BigDecimal((String) args.getOne("amount").get()).setScale(2, BigDecimal.ROUND_DOWN); - - TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(sender.getUniqueId()).get(); - TEAccount recipientAccount = (TEAccount) accountManager.getOrCreateAccount(recipient.getUniqueId()).get(); - - TransferResult transferResult = playerAccount.transfer(recipientAccount, accountManager.getDefaultCurrency(), amount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); - - if (transferResult.getResult() == ResultType.SUCCESS) { - sender.sendMessage(Text.of(TextColors.GRAY, "You have sent ", TextColors.GOLD, defaultCurrency.format(amount), - TextColors.GRAY, " to ", TextColors.GOLD, recipient.getName(), TextColors.GRAY, ".")); - - recipient.sendMessage(Text.of(TextColors.GRAY, "You have received ", TextColors.GOLD, defaultCurrency.format(amount), - TextColors.GRAY, " from ", TextColors.GOLD, sender.getName(), TextColors.GRAY, ".")); - } else if (transferResult.getResult() == ResultType.ACCOUNT_NO_FUNDS) { - sender.sendMessage(Text.of(TextColors.RED, "Insufficient funds.")); - } - } - } else { - sender.sendMessage(Text.of(TextColors.RED, "The amount must be positive.")); - } - } else { - sender.sendMessage(Text.of(TextColors.RED, "The amount must only contain numbers and a single decimal point if needed.")); + Player sender = (Player) src; + + if (sender.getUniqueId().equals(recipient.getUniqueId())) { + throw new CommandException(Text.of("You cannot pay yourself!")); } - } else if (src instanceof ConsoleSource || src instanceof CommandBlockSource) { - Object playerArg = args.getOne("player").get(); - String strAmount = (String) args.getOne("amount").get(); - if (playerArg instanceof Player) { - Player recipient = (Player) playerArg; - BigDecimal amount = new BigDecimal((String) args.getOne("amount").get()).setScale(2, BigDecimal.ROUND_DOWN); - Text amountText = Text.of(defaultCurrency.format(amount).toPlain().replace("-", "")); + Pattern amountPattern = Pattern.compile("^[+]?(\\d*\\.)?\\d+$"); + Matcher m = amountPattern.matcher(strAmount); + if (m.matches()) { + BigDecimal amount = new BigDecimal(strAmount).setScale(2, BigDecimal.ROUND_DOWN); + + TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(sender.getUniqueId()).get(); TEAccount recipientAccount = (TEAccount) accountManager.getOrCreateAccount(recipient.getUniqueId()).get(); - TransactionResult transactionResult = recipientAccount.deposit(accountManager.getDefaultCurrency(), amount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + TransferResult transferResult = playerAccount.transfer(recipientAccount, totalEconomy.getDefaultCurrency(), amount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + + if (transferResult.getResult() == ResultType.SUCCESS) { + sender.sendMessage(Text.of(TextColors.GRAY, "You have sent ", TextColors.GOLD, defaultCurrency.format(amount), + TextColors.GRAY, " to ", TextColors.GOLD, recipient.getName(), TextColors.GRAY, ".")); - if (transactionResult.getResult() == ResultType.SUCCESS) { - if (!strAmount.contains("-")) { - recipient.sendMessage(Text.of(TextColors.GRAY, "You have received ", TextColors.GOLD, amountText, - TextColors.GRAY, " from ", TextColors.GOLD, "SERVER.")); - } else { - recipient.sendMessage(Text.of(TextColors.GOLD, amountText, TextColors.GRAY, " has been removed from your account by the ", - TextColors.GOLD, "SERVER.")); - } + recipient.sendMessage(Text.of(TextColors.GRAY, "You have received ", TextColors.GOLD, defaultCurrency.format(amount), + TextColors.GRAY, " from ", TextColors.GOLD, sender.getName(), TextColors.GRAY, ".")); + + return CommandResult.success(); + } else if (transferResult.getResult() == ResultType.ACCOUNT_NO_FUNDS) { + throw new CommandException(Text.of("Insufficient funds!")); } + } else { + throw new CommandException(Text.of("Invalid amount! Must be a positive number!")); } } - return CommandResult.success(); + return CommandResult.empty(); } } diff --git a/src/main/java/com/erigitic/commands/SetBalanceCommand.java b/src/main/java/com/erigitic/commands/SetBalanceCommand.java index 25368fd2..1cdbac2a 100644 --- a/src/main/java/com/erigitic/commands/SetBalanceCommand.java +++ b/src/main/java/com/erigitic/commands/SetBalanceCommand.java @@ -33,7 +33,7 @@ import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.args.CommandContext; import org.spongepowered.api.command.spec.CommandExecutor; -import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.User; import org.spongepowered.api.event.cause.Cause; import org.spongepowered.api.event.cause.NamedCause; import org.spongepowered.api.service.economy.Currency; @@ -53,13 +53,13 @@ public SetBalanceCommand(TotalEconomy totalEconomy) { @Override public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { - Player recipient = args.getOne("player").get(); + User recipient = args.getOne("player").get(); BigDecimal amount = new BigDecimal(args.getOne("amount").get()).setScale(2, BigDecimal.ROUND_DOWN); - Currency defaultCurrency = accountManager.getDefaultCurrency(); + Currency defaultCurrency = totalEconomy.getDefaultCurrency(); TEAccount recipientAccount = (TEAccount) accountManager.getOrCreateAccount(recipient.getUniqueId()).get(); - recipientAccount.setBalance(accountManager.getDefaultCurrency(), amount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + recipientAccount.setBalance(totalEconomy.getDefaultCurrency(), amount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); src.sendMessage(Text.of(TextColors.GRAY, "You set ", recipient.getName(), "\'s balance to ", TextColors.GOLD, defaultCurrency.format(amount))); diff --git a/src/main/java/com/erigitic/commands/TEPermissions.java b/src/main/java/com/erigitic/commands/TEPermissions.java new file mode 100644 index 00000000..be9930ab --- /dev/null +++ b/src/main/java/com/erigitic/commands/TEPermissions.java @@ -0,0 +1,37 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.commands; + +public class TEPermissions { + + public static final String JOB_INFO = "totaleconomy.command.job.info"; + public static final String JOB_SET = "totaleconomy.command.job.set"; + // Some PermissionManagers seem to set defaults like this: + // (Due to a lack of time I was unable to verify this. But the suspicion is reasonable) + // 'some.permission.one' == 'some.permission.one.*' + // - MarkL4YG + public static final String JOB_SET_OTHERS = "totaleconomy.command.job.setother"; +} diff --git a/src/main/java/com/erigitic/commands/ViewBalanceCommand.java b/src/main/java/com/erigitic/commands/ViewBalanceCommand.java index c3250f8a..0ebd0852 100644 --- a/src/main/java/com/erigitic/commands/ViewBalanceCommand.java +++ b/src/main/java/com/erigitic/commands/ViewBalanceCommand.java @@ -57,8 +57,8 @@ public CommandResult execute(CommandSource sender, CommandContext args) throws C TEAccount recipientAccount = (TEAccount) accountManager.getOrCreateAccount(recipient.getUniqueId()).get(); - Currency defaultCurrency = accountManager.getDefaultCurrency(); - BigDecimal balance = recipientAccount.getBalance(accountManager.getDefaultCurrency()); + Currency defaultCurrency = totalEconomy.getDefaultCurrency(); + BigDecimal balance = recipientAccount.getBalance(defaultCurrency); sender.sendMessage(Text.of(TextColors.GRAY, recipient.getName(), "'s Balance: ", TextColors.GOLD, defaultCurrency.format(balance))); diff --git a/src/main/java/com/erigitic/config/AccountManager.java b/src/main/java/com/erigitic/config/AccountManager.java index ecf4cb5f..e0cd5073 100644 --- a/src/main/java/com/erigitic/config/AccountManager.java +++ b/src/main/java/com/erigitic/config/AccountManager.java @@ -26,11 +26,14 @@ package com.erigitic.config; import com.erigitic.main.TotalEconomy; +import com.erigitic.sql.SQLHandler; +import com.erigitic.sql.SQLQuery; import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.commented.CommentedConfigurationNode; import ninja.leaping.configurate.hocon.HoconConfigurationLoader; import ninja.leaping.configurate.loader.ConfigurationLoader; import org.slf4j.Logger; +import org.spongepowered.api.Sponge; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.service.context.ContextCalculator; import org.spongepowered.api.service.economy.Currency; @@ -46,6 +49,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeUnit; public class AccountManager implements EconomyService { private TotalEconomy totalEconomy; @@ -54,17 +58,43 @@ public class AccountManager implements EconomyService { private ConfigurationLoader loader; private ConfigurationNode accountConfig; + private SQLHandler sqlHandler; + + private boolean databaseActive; + + private boolean dirty = false; + + /** + * Constructor for the AccountManager class. Handles the initialization of necessary variables, setup of the database + * or configuration files depending on main configuration value, and starts save script if setup. + * + * @param totalEconomy Main plugin class + */ public AccountManager(TotalEconomy totalEconomy) { this.totalEconomy = totalEconomy; logger = totalEconomy.getLogger(); + databaseActive = totalEconomy.isDatabaseActive(); + + if (databaseActive) { + sqlHandler = totalEconomy.getSqlHandler(); - setupConfig(); + setupDatabase(); + } else { + setupConfig(); + } + + if (totalEconomy.getSaveInterval() > 0) { + Sponge.getScheduler().createTaskBuilder().interval(totalEconomy.getSaveInterval(), TimeUnit.SECONDS) + .execute(() -> { + writeToDisk(); + }).submit(totalEconomy); + } } /** - * Setup the config file that will contain the user accounts. + * Setup the config file that will contain the user accounts */ - public void setupConfig() { + private void setupConfig() { accountsFile = new File(totalEconomy.getConfigDir(), "accounts.conf"); loader = HoconConfigurationLoader.builder().setFile(accountsFile).build(); @@ -75,22 +105,59 @@ public void setupConfig() { loader.save(accountConfig); } } catch (IOException e) { - logger.warn("Could not create accounts config file!"); + logger.warn("[TE] Error creating accounts configuration file!"); } } + /** + * Setup the database that will contain the user accounts + */ + public void setupDatabase() { + sqlHandler.createDatabase(); + + sqlHandler.createTable("accounts", "uid varchar(60) NOT NULL," + + getDefaultCurrency().getName().toLowerCase() + "_balance decimal(19,2) NOT NULL DEFAULT '" + totalEconomy.getStartingBalance() + "'," + + "job varchar(50) NOT NULL DEFAULT 'Unemployed'," + + "job_notifications boolean NOT NULL DEFAULT TRUE," + + "PRIMARY KEY (uid)"); + + sqlHandler.createTable("virtual_accounts", "uid varchar(60) NOT NULL," + + getDefaultCurrency().getName().toLowerCase() + "_balance decimal(19,2) NOT NULL DEFAULT '" + totalEconomy.getStartingBalance() + "'," + + "PRIMARY KEY (uid)"); + + sqlHandler.createTable("levels", "uid varchar(60)," + + "miner int(10) unsigned NOT NULL DEFAULT '1'," + + "lumberjack int(10) unsigned NOT NULL DEFAULT '1'," + + "warrior int(10) unsigned NOT NULL DEFAULT '1'," + + "fisherman int(10) unsigned NOT NULL DEFAULT '1'," + + "FOREIGN KEY (uid) REFERENCES totaleconomy.accounts(uid) ON DELETE CASCADE"); + + sqlHandler.createTable("experience", "uid varchar(60)," + + "miner int(10) unsigned NOT NULL DEFAULT '0'," + + "lumberjack int(10) unsigned NOT NULL DEFAULT '0'," + + "warrior int(10) unsigned NOT NULL DEFAULT '0'," + + "fisherman int(10) unsigned NOT NULL DEFAULT '0'," + + "FOREIGN KEY (uid) REFERENCES totaleconomy.accounts(uid) ON DELETE CASCADE"); + } + /** * Reload the account config */ public void reloadConfig() { try { accountConfig = loader.load(); - logger.info("Reloading account configuration file."); + logger.info("[TE] Reloading account configuration file."); } catch (IOException e) { - logger.warn("Could not reload account configuration file!"); + logger.warn("[TE] An error occurred while reloading the account configuration file!"); } } + /** + * Gets or creates a unique account for the passed in UUID + * + * @param uuid {@link UUID} of the player an account is being created for + * @return Optional The account that was retrieved or created + */ @Override public Optional getOrCreateAccount(UUID uuid) { String currencyName = getDefaultCurrency().getDisplayName().toPlain().toLowerCase(); @@ -98,55 +165,127 @@ public Optional getOrCreateAccount(UUID uuid) { try { if (!hasAccount(uuid)) { - accountConfig.getNode(uuid.toString(), currencyName + "-balance").setValue(playerAccount.getDefaultBalance(getDefaultCurrency())); - accountConfig.getNode(uuid.toString(), "job").setValue("Unemployed"); - accountConfig.getNode(uuid.toString(), "jobnotifications").setValue(totalEconomy.hasJobNotifications()); - - loader.save(accountConfig); + if (databaseActive) { + SQLQuery.builder(sqlHandler.dataSource).insert("totaleconomy.accounts") + .columns("uid", currencyName + "_balance", "job", "job_notifications") + .values(uuid.toString(), playerAccount.getDefaultBalance(getDefaultCurrency()).toString(), "unemployed", String.valueOf(totalEconomy.hasJobNotifications())) + .build(); + + SQLQuery.builder(sqlHandler.dataSource).insert("totaleconomy.levels") + .columns("uid") + .values(uuid.toString()) + .build(); + + SQLQuery.builder(sqlHandler.dataSource).insert("totaleconomy.experience") + .columns("uid") + .values(uuid.toString()) + .build(); + } else { + accountConfig.getNode(uuid.toString(), currencyName + "-balance").setValue(playerAccount.getDefaultBalance(getDefaultCurrency())); + accountConfig.getNode(uuid.toString(), "job").setValue("unemployed"); + accountConfig.getNode(uuid.toString(), "jobnotifications").setValue(totalEconomy.hasJobNotifications()); + loader.save(accountConfig); + } } } catch (IOException e) { - logger.warn("Could not create account!"); + logger.warn("[TE] An error occurred while creating a new account!"); } return Optional.of(playerAccount); } + /** + * Gets or creates a virtual account for the passed in identifier + * + * @param identifier The virtual accounts identifier + * @return Optiona The virtual account that was retrieved or created + */ @Override public Optional getOrCreateAccount(String identifier) { String currencyName = getDefaultCurrency().getDisplayName().toPlain().toLowerCase(); TEVirtualAccount virtualAccount = new TEVirtualAccount(totalEconomy, this, identifier); try { - if (accountConfig.getNode(identifier, currencyName + "-balance").getValue() == null) { - accountConfig.getNode(identifier, currencyName + "-balance").setValue(virtualAccount.getDefaultBalance(getDefaultCurrency())); - - loader.save(accountConfig); + if (!hasAccount(identifier)) { + if (databaseActive) { + SQLQuery.builder(sqlHandler.dataSource).insert("totaleconomy.virtual_accounts") + .columns("uid", currencyName + "_balance") + .values(identifier, virtualAccount.getDefaultBalance(getDefaultCurrency()).toString()) + .build(); + } else { + accountConfig.getNode(identifier, currencyName + "-balance").setValue(virtualAccount.getDefaultBalance(getDefaultCurrency())); + loader.save(accountConfig); + } } } catch (IOException e) { - logger.warn("Could not create account!"); + logger.warn("[TE] An error occurred while creating a new virtual account!"); } return Optional.of(virtualAccount); } + /** + * Determines if a unique account is associated with the passed in UUID + * + * @param uuid {@link UUID} to check for an account + * @return boolean Whether or not an account is associated with the passed in UUID + */ @Override public boolean hasAccount(UUID uuid) { - return accountConfig.getNode(uuid.toString()).getValue() != null; + if (databaseActive) { + SQLQuery query = SQLQuery.builder(sqlHandler.dataSource) + .select("uid") + .from("totaleconomy.accounts") + .where("uid") + .equals(uuid.toString()) + .build(); + + return query.recordExists(); + } else { + return accountConfig.getNode(uuid.toString()).getValue() != null; + } } + /** + * Determines if a virtual account is associated with the passed in UUID + * + * @param identifier The identifier to check for an account + * @return boolean Whether or not a virtual account is associated with the passed in identifier + */ @Override public boolean hasAccount(String identifier) { - return accountConfig.getNode(identifier).getValue() != null; + if (databaseActive) { + SQLQuery query = SQLQuery.builder(sqlHandler.dataSource) + .select("uid") + .from("totaleconomy.virtual_accounts") + .where("uid") + .equals(identifier) + .build(); + + return query.recordExists(); + } else { + return accountConfig.getNode(identifier).getValue() != null; + } } + /** + * Gets the default {@link Currency} + * + * @return Currency The default currency + */ @Override public Currency getDefaultCurrency() { return totalEconomy.getDefaultCurrency(); } + /** + * Gets a set containing all of the currencies + * + * @return Set Set of all currencies + */ @Override public Set getCurrencies() { - return new HashSet(); + return new HashSet<>(); } @Override @@ -154,43 +293,91 @@ public void registerContextCalculator(ContextCalculator calculator) { } + /** + * Gets the passed in player's notification state + * + * @param player The {@link Player} who's notification state to get + * @return boolean The notification state + */ + public boolean getJobNotificationState(Player player) { + UUID playerUUID = player.getUniqueId(); + + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource).select("job_notifications") + .from("totaleconomy.accounts") + .where("uid") + .equals(playerUUID.toString()) + .build(); + + return sqlQuery.getBoolean(true); + } else { + return accountConfig.getNode(player.getUniqueId().toString(), "jobnotifications").getBoolean(true); + } + } + /** * Toggle a player's exp/money notifications for jobs * - * @param player an object representing the player toggling notifications + * @param player Player toggling notifications */ public void toggleNotifications(Player player) { - boolean notify = accountConfig.getNode(player.getUniqueId().toString(), "jobnotifications").getBoolean(); + boolean jobNotifications = !getJobNotificationState(player); + UUID playerUUID = player.getUniqueId(); + + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource).update("totaleconomy.accounts") + .set("job_notifications") + .equals(jobNotifications ? "1":"0") + .where("uid") + .equals(playerUUID.toString()) + .build(); + + if (sqlQuery.getRowsAffected() <= 0) { + player.sendMessage(Text.of(TextColors.RED, "[TE] Error toggling notifications! Try again. If this keeps showing up, notify the server owner or plugin developer.")); + logger.warn("[TE] An error occurred while updating the notification state in the database!"); + } + } else { + accountConfig.getNode(player.getUniqueId().toString(), "jobnotifications").setValue(jobNotifications); + + try { + loader.save(accountConfig); + } catch (IOException e) { + player.sendMessage(Text.of(TextColors.RED, "[TE] Error toggling notifications! Try again. If this keeps showing up, notify the server owner or plugin developer.")); + logger.warn("[TE] An error occurred while updating the notification state!"); + } + } - if (notify == true) { - accountConfig.getNode(player.getUniqueId().toString(), "jobnotifications").setValue(false); - notify = false; + if (jobNotifications == true) { + player.sendMessage(Text.of(TextColors.GRAY, "Notifications are now ", TextColors.GREEN, "ON")); } else { - accountConfig.getNode(player.getUniqueId().toString(), "jobnotifications").setValue(true); - notify = true; + player.sendMessage(Text.of(TextColors.GRAY, "Notifications are now ", TextColors.RED, "OFF")); } + } - try { - loader.save(accountConfig); + /** + * Save the account configuration file + */ + public void saveAccountConfig() { + dirty = true; - if (notify == true) - player.sendMessage(Text.of(TextColors.GRAY, "Notifications are now ", TextColors.GREEN, "ON")); - else - player.sendMessage(Text.of(TextColors.GRAY, "Notifications are now ", TextColors.RED, "OFF")); - } catch (IOException e) { - player.sendMessage(Text.of(TextColors.RED, "Error toggling notifications! Try again. If this keeps showing up, notify the server owner or plugin developer.")); - logger.warn("Could not save notification change!"); + if (totalEconomy.getSaveInterval() <= 0) { + writeToDisk(); } } /** * Save the account configuration file */ - public void saveAccountConfig() { + public void writeToDisk() { + if (!dirty) { + return; + } + try { loader.save(accountConfig); + dirty = false; } catch (IOException e) { - logger.error("Could not save the account configuration file!"); + logger.error("[TE] An error occurred while saving the account configuration file!"); } } diff --git a/src/main/java/com/erigitic/config/TEAccount.java b/src/main/java/com/erigitic/config/TEAccount.java index 8740f5a8..ff42a1cf 100644 --- a/src/main/java/com/erigitic/config/TEAccount.java +++ b/src/main/java/com/erigitic/config/TEAccount.java @@ -26,9 +26,11 @@ package com.erigitic.config; import com.erigitic.main.TotalEconomy; +import com.erigitic.sql.SQLHandler; +import com.erigitic.sql.SQLQuery; import ninja.leaping.configurate.ConfigurationNode; -import org.slf4j.Logger; import org.spongepowered.api.event.cause.Cause; +import org.spongepowered.api.event.cause.NamedCause; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.economy.Currency; import org.spongepowered.api.service.economy.account.Account; @@ -44,18 +46,36 @@ public class TEAccount implements UniqueAccount { private TotalEconomy totalEconomy; private AccountManager accountManager; private UUID uuid; - private Logger logger; + private SQLHandler sqlHandler; private ConfigurationNode accountConfig; + private boolean databaseActive; + + /** + * Constructor for the TEAccount class. Manages a unique account, identified by a {@link UUID}, that contains balances for each {@link Currency}. + * + * @param totalEconomy Main plugin class + * @param accountManager {@link AccountManager} object + * @param uuid The UUID of the account + */ public TEAccount(TotalEconomy totalEconomy, AccountManager accountManager, UUID uuid) { this.totalEconomy = totalEconomy; this.accountManager = accountManager; this.uuid = uuid; accountConfig = accountManager.getAccountConfig(); + databaseActive = totalEconomy.isDatabaseActive(); + + if (databaseActive) + sqlHandler = totalEconomy.getSqlHandler(); } + /** + * Gets the display name associated with the account + * + * @return Text The display name + */ @Override public Text getDisplayName() { if (totalEconomy.getUserStorageService().get(uuid).isPresent()) @@ -64,29 +84,68 @@ public Text getDisplayName() { return Text.of("PLAYER NAME"); } + /** + * Gets the default balance + * + * @param currency Currency to get the default balance for + * @return BigDecimal Default balance + */ @Override public BigDecimal getDefaultBalance(Currency currency) { return totalEconomy.getStartingBalance(); } + /** + * Determines if a balance exists for a {@link Currency} + * + * @param currency Currency type to be checked for + * @param contexts + * @return boolean If a balance exists for the specified currency + */ @Override public boolean hasBalance(Currency currency, Set contexts) { String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - if (accountConfig.getNode(uuid.toString(), currencyName + "-balance").getValue() != null) { - return true; + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .select(currencyName + "_balance") + .from("totaleconomy.accounts") + .where("uid") + .equals(uuid.toString()) + .build(); + + return sqlQuery.recordExists(); + } else { + return accountConfig.getNode(uuid.toString(), currencyName + "-balance").getValue() != null; } - - return false; } + /** + * Gets the balance of a {@link Currency} + * + * @param currency The currency to get the balance of + * @param contexts + * @return BigDecimal The balance + */ @Override public BigDecimal getBalance(Currency currency, Set contexts) { if (hasBalance(currency, contexts)) { String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - BigDecimal balance = new BigDecimal(accountConfig.getNode(uuid.toString(), currencyName + "-balance").getString()); - return balance; + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .select(currencyName + "_balance") + .from("totaleconomy.accounts") + .where("uid") + .equals(uuid.toString()) + .build(); + + return sqlQuery.getBigDecimal(BigDecimal.ZERO); + } else { + BigDecimal balance = new BigDecimal(accountConfig.getNode(uuid.toString(), currencyName + "-balance").getString()); + + return balance; + } } return BigDecimal.ZERO; @@ -94,111 +153,141 @@ public BigDecimal getBalance(Currency currency, Set contexts) { @Override public Map getBalances(Set contexts) { - return new HashMap(); + return new HashMap<>(); } + /** + * Sets the balance of a {@link Currency} + * + * @param currency Currency to set the balance of + * @param amount Amount to set the balance to + * @param cause + * @param contexts + * @return + */ @Override public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause cause, Set contexts) { TransactionResult transactionResult; + String currencyName = currency.getDisplayName().toPlain().toLowerCase(); if (hasBalance(currency, contexts)) { - String currencyName = currency.getDisplayName().toPlain().toLowerCase(); + BigDecimal delta = amount.subtract(getBalance(currency)); + TransactionType transactionType; - accountConfig.getNode(uuid.toString(), currencyName + "-balance").setValue(amount.setScale(2, BigDecimal.ROUND_DOWN)); - accountManager.saveAccountConfig(); + if (delta.compareTo(BigDecimal.ZERO) >= 0) { + transactionType = TransactionTypes.DEPOSIT; + } else { + transactionType = TransactionTypes.WITHDRAW; + } - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.SUCCESS, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .update("totaleconomy.accounts") + .set(currencyName + "_balance") + .equals(amount.setScale(2, BigDecimal.ROUND_DOWN).toPlainString()) + .where("uid") + .equals(uuid.toString()) + .build(); + + if (sqlQuery.getRowsAffected() > 0) { + transactionResult = new TETransactionResult(this, currency, delta.abs(), contexts, ResultType.SUCCESS, transactionType); + } else { + transactionResult = new TETransactionResult(this, currency, delta.abs(), contexts, ResultType.FAILED, transactionType); + } + } else { + accountConfig.getNode(uuid.toString(), currencyName + "-balance").setValue(amount.setScale(2, BigDecimal.ROUND_DOWN)); + accountManager.saveAccountConfig(); - return transactionResult; + transactionResult = new TETransactionResult(this, currency, delta.abs(), contexts, ResultType.SUCCESS, transactionType); + } + } else { + transactionResult = new TETransactionResult(this, currency, BigDecimal.ZERO, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); } - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); return transactionResult; } + /** + * NOT IMPLEMENTED + * + * @param cause + * @param contexts + * @return + */ @Override public Map resetBalances(Cause cause, Set contexts) { - TransactionResult transactionResult = new TETransactionResult(this, accountManager.getDefaultCurrency(), BigDecimal.ZERO, contexts, ResultType.FAILED, TransactionTypes.WITHDRAW); + TransactionResult transactionResult = new TETransactionResult(this, totalEconomy.getDefaultCurrency(), BigDecimal.ZERO, contexts, ResultType.FAILED, TransactionTypes.WITHDRAW); totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - //TODO: Do something different here? Map result = new HashMap<>(); - result.put(accountManager.getDefaultCurrency(), transactionResult); + result.put(totalEconomy.getDefaultCurrency(), transactionResult); return result; } + /** + * Reset a balance + * + * @param currency The balance to reset + * @param cause + * @param contexts + * @return TransactionResult Result of the reset + */ @Override public TransactionResult resetBalance(Currency currency, Cause cause, Set contexts) { return setBalance(currency, BigDecimal.ZERO, cause); } + /** + * Add money to a balance + * + * @param currency The balance to deposit money into + * @param amount Amount to deposit + * @param cause + * @param contexts + * @return TransactionResult Result of the deposit + */ @Override public TransactionResult deposit(Currency currency, BigDecimal amount, Cause cause, Set contexts) { - TransactionResult transactionResult; - - if (hasBalance(currency, contexts)) { - String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - BigDecimal curBalance = getBalance(currency, contexts); - BigDecimal newBalance = curBalance.add(amount); - - accountConfig.getNode(uuid.toString(), currencyName + "-balance").setValue(newBalance.setScale(2, BigDecimal.ROUND_DOWN)); - - // Reset balance to the money cap if it goes over - if (totalEconomy.isLoadMoneyCap()) { - if (getBalance(currency, contexts).compareTo(totalEconomy.getMoneyCap()) == 1) { - accountConfig.getNode(uuid.toString(), currencyName + "-balance").setValue(totalEconomy.getMoneyCap()); - } - } - - accountManager.saveAccountConfig(); - - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.SUCCESS, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; - } - - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); + BigDecimal curBalance = getBalance(currency, contexts); + BigDecimal newBalance = curBalance.add(amount); - return transactionResult; + return setBalance(currency, newBalance, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); } + /** + * Remove money from a balance + * + * @param currency The balance to withdraw money from + * @param amount Amount to withdraw + * @param cause + * @param contexts + * @return TransactionResult Result of the withdrawal + */ @Override public TransactionResult withdraw(Currency currency, BigDecimal amount, Cause cause, Set contexts) { - TransactionResult transactionResult; + BigDecimal curBalance = getBalance(currency, contexts); + BigDecimal newBalance = curBalance.subtract(amount); - if (hasBalance(currency, contexts)) { - String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - BigDecimal curBalance = getBalance(currency, contexts); - BigDecimal newBalance = curBalance.subtract(amount); - - if (newBalance.compareTo(BigDecimal.ZERO) >= 0) { - accountConfig.getNode(uuid.toString(), currencyName + "-balance").setValue(newBalance.setScale(2, BigDecimal.ROUND_DOWN)); - accountManager.saveAccountConfig(); - - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.SUCCESS, TransactionTypes.WITHDRAW); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; - } else { - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; - } + if (newBalance.compareTo(BigDecimal.ZERO) >= 0) { + return setBalance(currency, newBalance, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); } - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; + return new TETransactionResult(this, currency, amount, contexts, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW); } + /** + * Transfer money between two TEAccount's + * + * @param to Account to transfer money to + * @param currency Type of currency to transfer + * @param amount Amount to transfer + * @param cause + * @param contexts + * @return TransactionResult Result of the reset + */ @Override public TransferResult transfer(Account to, Currency currency, BigDecimal amount, Cause cause, Set contexts) { TransferResult transferResult; @@ -207,7 +296,6 @@ public TransferResult transfer(Account to, Currency currency, BigDecimal amount, BigDecimal curBalance = getBalance(currency, contexts); BigDecimal newBalance = curBalance.subtract(amount); - //TODO: Might not need to check if the balance is greater then zero here since it is being done in the withdraw function if (newBalance.compareTo(BigDecimal.ZERO) >= 0) { withdraw(currency, amount, cause, contexts); @@ -238,11 +326,21 @@ public TransferResult transfer(Account to, Currency currency, BigDecimal amount, return transferResult; } + /** + * Get the account identifier + * + * @return String The identifier + */ @Override public String getIdentifier() { return uuid.toString(); } + /** + * Get the {@link UUID} of the account + * + * @return UUID The UUID of the account + */ @Override public UUID getUniqueId() { return uuid; @@ -250,6 +348,6 @@ public UUID getUniqueId() { @Override public Set getActiveContexts() { - return new HashSet(); + return new HashSet<>(); } } diff --git a/src/main/java/com/erigitic/config/TECurrency.java b/src/main/java/com/erigitic/config/TECurrency.java index 287c429d..6b2b4953 100644 --- a/src/main/java/com/erigitic/config/TECurrency.java +++ b/src/main/java/com/erigitic/config/TECurrency.java @@ -39,13 +39,15 @@ public class TECurrency implements Currency { private Text symbol; private int numFractionDigits; private boolean defaultCurrency; + private boolean prefixSymbol; - public TECurrency(Text singular, Text plural, Text symbol, int numFractionDigits, boolean defaultCurrency) { + public TECurrency(Text singular, Text plural, Text symbol, int numFractionDigits, boolean defaultCurrency, boolean prefixSymbol) { this.singular = singular; this.plural = plural; this.symbol = symbol; this.numFractionDigits = numFractionDigits; this.defaultCurrency = defaultCurrency; + this.prefixSymbol = prefixSymbol; } @Override @@ -75,7 +77,12 @@ public Text getSymbol() { @Override public Text format(BigDecimal amount, int numFractionDigits) { - return Text.of(symbol, NumberFormat.getInstance(Locale.ENGLISH).format(amount.setScale(numFractionDigits, BigDecimal.ROUND_HALF_UP))); + if (prefixSymbol) { + return Text.of(symbol, amount.setScale(numFractionDigits, BigDecimal.ROUND_HALF_UP)); + } else { + return Text.of(amount.setScale(numFractionDigits, BigDecimal.ROUND_HALF_UP), symbol); + + } } @Override @@ -83,7 +90,6 @@ public int getDefaultFractionDigits() { return numFractionDigits; } - @Override public boolean isDefault() { return defaultCurrency; diff --git a/src/main/java/com/erigitic/config/TEVirtualAccount.java b/src/main/java/com/erigitic/config/TEVirtualAccount.java index ba1a6e7c..578e1cb2 100644 --- a/src/main/java/com/erigitic/config/TEVirtualAccount.java +++ b/src/main/java/com/erigitic/config/TEVirtualAccount.java @@ -26,34 +26,42 @@ package com.erigitic.config; import com.erigitic.main.TotalEconomy; +import com.erigitic.sql.SQLHandler; +import com.erigitic.sql.SQLQuery; import ninja.leaping.configurate.ConfigurationNode; import org.spongepowered.api.event.cause.Cause; +import org.spongepowered.api.event.cause.NamedCause; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.economy.Currency; import org.spongepowered.api.service.economy.account.Account; import org.spongepowered.api.service.economy.account.VirtualAccount; -import org.spongepowered.api.service.economy.transaction.ResultType; -import org.spongepowered.api.service.economy.transaction.TransactionResult; -import org.spongepowered.api.service.economy.transaction.TransactionTypes; -import org.spongepowered.api.service.economy.transaction.TransferResult; +import org.spongepowered.api.service.economy.transaction.*; import org.spongepowered.api.text.Text; import java.math.BigDecimal; import java.util.*; public class TEVirtualAccount implements VirtualAccount { + private TotalEconomy totalEconomy; private AccountManager accountManager; private String identifier; + private SQLHandler sqlHandler; private ConfigurationNode accountConfig; + private boolean databaseActive; + public TEVirtualAccount(TotalEconomy totalEconomy, AccountManager accountManager, String identifier) { this.totalEconomy = totalEconomy; this.accountManager = accountManager; this.identifier = identifier; accountConfig = accountManager.getAccountConfig(); + databaseActive = totalEconomy.isDatabaseActive(); + + if (databaseActive) + sqlHandler = totalEconomy.getSqlHandler(); } @Override @@ -63,27 +71,46 @@ public Text getDisplayName() { @Override public BigDecimal getDefaultBalance(Currency currency) { - return BigDecimal.ZERO; + return totalEconomy.getStartingBalance(); } @Override public boolean hasBalance(Currency currency, Set contexts) { String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - if (accountConfig.getNode(identifier, currencyName + "-balance").getValue() != null) { - return true; + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .select(currencyName + "_balance") + .from("totaleconomy.virtual_accounts") + .where("uid") + .equals(identifier) + .build(); + + return sqlQuery.recordExists(); + } else { + return accountConfig.getNode(identifier, currencyName + "-balance").getValue() != null; } - - return false; } @Override public BigDecimal getBalance(Currency currency, Set contexts) { if (hasBalance(currency, contexts)) { String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - BigDecimal balance = new BigDecimal(accountConfig.getNode(identifier, currencyName + "-balance").getString()); - return balance; + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .select(currencyName + "_balance") + .from("totaleconomy.virtual_accounts") + .where("uid") + .equals(identifier) + .build(); + + return sqlQuery.getBigDecimal(BigDecimal.ZERO); + } else { + BigDecimal balance = new BigDecimal(accountConfig.getNode(identifier, currencyName + "-balance").getString()); + + return balance; + } } return BigDecimal.ZERO; @@ -91,26 +118,48 @@ public BigDecimal getBalance(Currency currency, Set contexts) { @Override public Map getBalances(Set contexts) { - return new HashMap(); + return new HashMap<>(); } @Override public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause cause, Set contexts) { TransactionResult transactionResult; + String currencyName = currency.getDisplayName().toPlain().toLowerCase(); if (hasBalance(currency, contexts)) { - String currencyName = currency.getDisplayName().toPlain().toLowerCase(); + BigDecimal delta = amount.subtract(getBalance(currency)); + TransactionType transactionType; - accountConfig.getNode(identifier, currencyName + "-balance").setValue(amount.setScale(2, BigDecimal.ROUND_DOWN)); - accountManager.saveAccountConfig(); + if (delta.compareTo(BigDecimal.ZERO) >= 0) { + transactionType = TransactionTypes.DEPOSIT; + } else { + transactionType = TransactionTypes.WITHDRAW; + } - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.SUCCESS, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .update("totaleconomy.virtual_accounts") + .set(currencyName + "_balance") + .equals(amount.setScale(2, BigDecimal.ROUND_DOWN).toPlainString()) + .where("uid") + .equals(identifier) + .build(); + + if (sqlQuery.getRowsAffected() > 0) { + transactionResult = new TETransactionResult(this, currency, delta.abs(), contexts, ResultType.SUCCESS, transactionType); + } else { + transactionResult = new TETransactionResult(this, currency, delta.abs(), contexts, ResultType.FAILED, transactionType); + } + } else { + accountConfig.getNode(identifier, currencyName + "-balance").setValue(amount.setScale(2, BigDecimal.ROUND_DOWN)); + accountManager.saveAccountConfig(); - return transactionResult; + transactionResult = new TETransactionResult(this, currency, delta.abs(), contexts, ResultType.SUCCESS, transactionType); + } + } else { + transactionResult = new TETransactionResult(this, currency, BigDecimal.ZERO, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); } - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); return transactionResult; @@ -118,11 +167,11 @@ public TransactionResult setBalance(Currency currency, BigDecimal amount, Cause @Override public Map resetBalances(Cause cause, Set contexts) { - TransactionResult transactionResult = new TETransactionResult(this, accountManager.getDefaultCurrency(), BigDecimal.ZERO, contexts, ResultType.FAILED, TransactionTypes.WITHDRAW); + TransactionResult transactionResult = new TETransactionResult(this, totalEconomy.getDefaultCurrency(), BigDecimal.ZERO, contexts, ResultType.FAILED, TransactionTypes.WITHDRAW); totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - HashMap result = new HashMap(); - result.put(accountManager.getDefaultCurrency(), transactionResult); + Map result = new HashMap<>(); + result.put(totalEconomy.getDefaultCurrency(), transactionResult); return result; } @@ -134,57 +183,22 @@ public TransactionResult resetBalance(Currency currency, Cause cause, Set contexts) { - TransactionResult transactionResult; - - if (hasBalance(currency, contexts)) { - String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - BigDecimal curBalance = getBalance(currency, contexts); - BigDecimal newBalance = curBalance.add(amount); + BigDecimal curBalance = getBalance(currency, contexts); + BigDecimal newBalance = curBalance.add(amount); - accountConfig.getNode(identifier, currencyName + "-balance").setValue(newBalance.setScale(2, BigDecimal.ROUND_DOWN)); - accountManager.saveAccountConfig(); - - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.SUCCESS, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; - } - - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; + return setBalance(currency, newBalance, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); } @Override public TransactionResult withdraw(Currency currency, BigDecimal amount, Cause cause, Set contexts) { - TransactionResult transactionResult; - - if (hasBalance(currency, contexts)) { - String currencyName = currency.getDisplayName().toPlain().toLowerCase(); - BigDecimal curBalance = getBalance(currency, contexts); - BigDecimal newBalance = curBalance.subtract(amount); - - if (newBalance.compareTo(BigDecimal.ZERO) >= 0) { - accountConfig.getNode(identifier, currencyName + "-balance").setValue(newBalance.setScale(2, BigDecimal.ROUND_DOWN)); - accountManager.saveAccountConfig(); - - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.SUCCESS, TransactionTypes.WITHDRAW); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); + BigDecimal curBalance = getBalance(currency, contexts); + BigDecimal newBalance = curBalance.subtract(amount); - return transactionResult; - } else { - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; - } + if (newBalance.compareTo(BigDecimal.ZERO) >= 0) { + return setBalance(currency, newBalance, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); } - transactionResult = new TETransactionResult(this, currency, amount, contexts, ResultType.FAILED, TransactionTypes.DEPOSIT); - totalEconomy.getGame().getEventManager().post(new TEEconomyTransactionEvent(transactionResult)); - - return transactionResult; + return new TETransactionResult(this, currency, amount, contexts, ResultType.ACCOUNT_NO_FUNDS, TransactionTypes.WITHDRAW); } @Override diff --git a/src/main/java/com/erigitic/jobs/JobBasedRequirement.java b/src/main/java/com/erigitic/jobs/JobBasedRequirement.java new file mode 100644 index 00000000..d7d5569a --- /dev/null +++ b/src/main/java/com/erigitic/jobs/JobBasedRequirement.java @@ -0,0 +1,62 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs; + +import ninja.leaping.configurate.ConfigurationNode; + +/** + * Requirement notation that is usable in various places such as higher job tiers + */ +public class JobBasedRequirement { + private int reqJobLevel; + private String reqJob; + private String reqPermission; + + public JobBasedRequirement(String reqJob, int reqJobLevel, String reqPermission) { + this.reqJob = reqJob; + this.reqJobLevel = reqJobLevel; + this.reqPermission = reqPermission; + } + + public int jobLevelNeeded() { + return reqJobLevel; + } + + public String jobNeeded() { + return reqJob; + } + + public String permissionNeeded() { + return reqPermission; + } + + public void addTo(ConfigurationNode node) { + node = node.getNode("require"); + node.getNode("job").setValue(reqJob); + node.getNode("level").setValue(reqJobLevel); + node.getNode("permission").setValue(reqPermission); + } +} diff --git a/src/main/java/com/erigitic/jobs/TEActionReward.java b/src/main/java/com/erigitic/jobs/TEActionReward.java new file mode 100644 index 00000000..23f5a5de --- /dev/null +++ b/src/main/java/com/erigitic/jobs/TEActionReward.java @@ -0,0 +1,76 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs; + +import ninja.leaping.configurate.ConfigurationNode; + +import java.math.BigDecimal; +import java.util.Optional; + +public class TEActionReward { + private String action; + private String targetID; + private int expReward; + private BigDecimal moneyReward; + private String growthTrait; + private Integer growthMeta; + + private boolean isValid = false; + + public TEActionReward(String action, String targetID, String expReward, String moneyReward, String growthTrait) { + this.action = action; + this.targetID = targetID; + this.expReward = Integer.parseInt(expReward); + this.moneyReward = new BigDecimal(moneyReward); + this.growthTrait = growthTrait; + + isValid = (action != null && !action.isEmpty() && targetID != null && !targetID.isEmpty()); + } + + public String getAction() { + return action; + } + + public String getTargetID() { + return targetID; + } + + public int getExpReward() { + return expReward; + } + + public BigDecimal getMoneyReward() { + return moneyReward; + } + + public boolean isValid() { + return isValid; + } + + public Optional getGrowthTrait() { + return Optional.ofNullable(growthTrait); + } +} diff --git a/src/main/java/com/erigitic/jobs/TEJob.java b/src/main/java/com/erigitic/jobs/TEJob.java new file mode 100644 index 00000000..242407ef --- /dev/null +++ b/src/main/java/com/erigitic/jobs/TEJob.java @@ -0,0 +1,81 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs; + +import com.google.common.reflect.TypeToken; +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.objectmapping.ObjectMappingException; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class TEJob { + private String name; + private BigDecimal salary; + private List sets = new ArrayList<>(); + private JobBasedRequirement requirement; + + private boolean isValid = false; + + public TEJob(ConfigurationNode node) { + name = node.getKey().toString(); + salary = new BigDecimal(node.getNode("salary").getString()); + + try { + sets = node.getNode("sets").getList(TypeToken.of(String.class), new ArrayList<>()); + + isValid = true; + } catch (ObjectMappingException e) { + isValid = false; + + e.printStackTrace(); + } + } + + public List getSets() { + return sets; + } + + public boolean salaryEnabled() { + return !salary.equals(BigDecimal.ZERO); + } + + public String getName() { return name; } + + public BigDecimal getSalary() { + return salary; + } + + public Optional getRequirement() { + return Optional.ofNullable(requirement); + } + + public boolean isValid() { + return isValid; + } +} diff --git a/src/main/java/com/erigitic/jobs/TEJobManager.java b/src/main/java/com/erigitic/jobs/TEJobManager.java new file mode 100644 index 00000000..5c5601a2 --- /dev/null +++ b/src/main/java/com/erigitic/jobs/TEJobManager.java @@ -0,0 +1,974 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs; + +import com.erigitic.config.AccountManager; +import com.erigitic.config.TEAccount; +import com.erigitic.jobs.jobs.*; +import com.erigitic.jobs.jobsets.*; +import com.erigitic.main.TotalEconomy; +import com.erigitic.sql.SQLHandler; +import com.erigitic.sql.SQLQuery; +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.commented.CommentedConfigurationNode; +import ninja.leaping.configurate.hocon.HoconConfigurationLoader; +import ninja.leaping.configurate.loader.ConfigurationLoader; +import org.slf4j.Logger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.tileentity.Sign; +import org.spongepowered.api.block.tileentity.TileEntity; +import org.spongepowered.api.block.trait.BlockTrait; +import org.spongepowered.api.block.trait.IntegerTrait; +import org.spongepowered.api.data.Transaction; +import org.spongepowered.api.data.manipulator.mutable.item.FishData; +import org.spongepowered.api.data.manipulator.mutable.tileentity.SignData; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.User; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.action.FishingEvent; +import org.spongepowered.api.event.block.ChangeBlockEvent; +import org.spongepowered.api.event.block.InteractBlockEvent; +import org.spongepowered.api.event.block.tileentity.ChangeSignEvent; +import org.spongepowered.api.event.cause.Cause; +import org.spongepowered.api.event.cause.NamedCause; +import org.spongepowered.api.event.cause.entity.damage.source.EntityDamageSource; +import org.spongepowered.api.event.entity.DestructEntityEvent; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; +import org.spongepowered.api.scheduler.Scheduler; +import org.spongepowered.api.scheduler.Task; +import org.spongepowered.api.service.economy.transaction.ResultType; +import org.spongepowered.api.service.economy.transaction.TransactionResult; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.action.TextActions; +import org.spongepowered.api.text.format.TextColors; +import org.spongepowered.api.service.economy.Currency; + +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class TEJobManager { + + // We only need one instance of this + public static final JobSet[] defaultJobSets = { + new FishermanJobSet(), + new LumberjackJobSet(), + new MinerJobSet(), + new WarriorJobSet(), + new FarmerJobSet() + }; + + // We only need one instance of this + private final Job[] defaultJobsArr = { + new UnemployedJob(), + new FishermanJob(), + new LumberjackJob(), + new MinerJob(), + new WarriorJob(), + new FarmerJob() + }; + + private TotalEconomy totalEconomy; + private AccountManager accountManager; + private ConfigurationNode accountConfig; + private Logger logger; + + private File jobSetsFile; + private ConfigurationLoader jobSetsLoader; + private ConfigurationNode jobSetsConfig; + private Map jobSets; + + private File jobsFile; + private ConfigurationLoader jobsLoader; + private ConfigurationNode jobsConfig; + private Map jobsMap; + + private boolean databaseActive; + private SQLHandler sqlHandler; + + public TEJobManager(TotalEconomy totalEconomy) { + this.totalEconomy = totalEconomy; + + accountManager = totalEconomy.getAccountManager(); + accountConfig = accountManager.getAccountConfig(); + logger = totalEconomy.getLogger(); + databaseActive = totalEconomy.isDatabaseActive(); + + if (databaseActive) + sqlHandler = totalEconomy.getSqlHandler(); + + setupConfig(); + + if (totalEconomy.isLoadSalary()) + startSalaryTask(); + } + + /** + * Start the timer that pays out the salary to each player after a specified time in seconds + */ + private void startSalaryTask() { + Scheduler scheduler = totalEconomy.getGame().getScheduler(); + Task.Builder payTask = scheduler.createTaskBuilder(); + + payTask.execute(() -> { + for (Player player : totalEconomy.getServer().getOnlinePlayers()) { + Optional optJob = getJob(getPlayerJob(player), true); + + if (!optJob.isPresent()) { + player.sendMessage(Text.of(TextColors.RED, "[TE] Cannot pay your salary! Contact your administrator!")); + + return; + } + + if (optJob.get().salaryEnabled()) { + BigDecimal salary = optJob.get().getSalary(); + TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); + + TransactionResult result = playerAccount.deposit(totalEconomy.getDefaultCurrency(), salary, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + + if (result.getResult() == ResultType.SUCCESS) { + player.sendMessage(Text.of(TextColors.GRAY, "Your salary of ", TextColors.GOLD, totalEconomy.getDefaultCurrency().format(salary), TextColors.GRAY, " has just been paid.")); + } else { + player.sendMessage(Text.of(TextColors.RED, "[TE] Failed to pay your salary! You may want to contact your admin - TransactionResult: ", result.getResult().toString())); + } + } + } + }).delay(jobsConfig.getNode("salarydelay").getInt(), TimeUnit.SECONDS).interval(jobsConfig.getNode("salarydelay").getInt(), TimeUnit.SECONDS).name("Pay Day").submit(totalEconomy); + } + + /** + * Setup the jobs config + */ + public void setupConfig() { + jobSetsFile = new File(totalEconomy.getConfigDir(), "jobSets.conf"); + jobSetsLoader = HoconConfigurationLoader.builder().setFile(jobSetsFile).build(); + jobSets = new HashMap(); + reloadJobSetConfig(); + + jobsFile = new File(totalEconomy.getConfigDir(), "jobs.conf"); + jobsLoader = HoconConfigurationLoader.builder().setFile(jobsFile).build(); + jobsMap = new HashMap(); + reloadJobsConfig(); + } + + /** + * Reload the jobSet config + */ + public boolean reloadJobSetConfig() { + try { + jobSetsConfig = jobSetsLoader.load(); + ConfigurationNode sets = jobSetsConfig.getNode("sets"); + + if (!jobSetsFile.exists()) { + for (JobSet s : defaultJobSets) { + s.populateNode(sets); + } + + jobSetsLoader.save(jobSetsConfig); + } + + sets.getChildrenMap().forEach((setName, setNode) -> { + if (setNode != null) { + TEJobSet jobSet = new TEJobSet(setNode); + + jobSets.put((String) setName, jobSet); + } + }); + + return true; + } catch (IOException e) { + logger.warn("[TE] An error occurred while creating/loading the jobSets configuration file!"); + + return false; + } + } + + /** + * Reloads the job configuration file. Can be used for initial creation of the configuration file + * or for simply reloading it. + * + * @return boolean Was the reload successful? + */ + public boolean reloadJobsConfig() { + try { + jobsConfig = jobsLoader.load(); + ConfigurationNode jobsNode = jobsConfig.getNode("jobs"); + + if (!jobsFile.exists()) { + for (Job j : defaultJobsArr) { + j.populateNode(jobsNode); + } + + jobsConfig.getNode("salarydelay").setValue(300); + + jobsLoader.save(jobsConfig); + } + + // Loop through each job node in the configuration file, create a TEJob object from it, and store in a HashMap + jobsNode.getChildrenMap().forEach((k, jobNode) -> { + if (jobNode != null) { + TEJob job = new TEJob(jobNode); + + if (job.isValid()) { + jobsMap.put(job.getName(), job); + } + } + }); + + return true; + } catch (IOException e) { + logger.warn("[TE] An error occurred while creating/loading the jobs configuration file!"); + + return false; + } + } + + /** + * Reload all job configs (jobs + sets) + */ + public boolean reloadJobsAndSets() { + return reloadJobsConfig() && reloadJobSetConfig(); + } + + /** + * Add exp to player's current job + * + * @param player The player to give experience to + * @param expAmount The amount of experience to add + */ + public void addExp(Player player, int expAmount) { + String jobName = getPlayerJob(player); + UUID playerUUID = player.getUniqueId(); + boolean jobNotifications = accountManager.getJobNotificationState(player); + + if (databaseActive) { + int newExp = getJobExp(jobName, player) + expAmount; + + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .update("totaleconomy.experience") + .set(jobName) + .equals(String.valueOf(newExp)) + .where("uid") + .equals(playerUUID.toString()) + .build(); + + if (sqlQuery.getRowsAffected() > 0) { + if (jobNotifications) { + player.sendMessage(Text.of(TextColors.GRAY, "You have gained ", TextColors.GOLD, expAmount, TextColors.GRAY, " exp in the ", TextColors.GOLD, jobName, TextColors.GRAY, " job.")); + } + } else { + logger.warn("[TE] An error occurred while updating job experience in the database!"); + player.sendMessage(Text.of(TextColors.RED, "[TE] Error adding experience! Consult an administrator!")); + } + } else { + int curExp = accountConfig.getNode(playerUUID.toString(), "jobstats", jobName, "exp").getInt(); + + accountConfig.getNode(playerUUID.toString(), "jobstats", jobName, "exp").setValue(curExp + expAmount); + + if (jobNotifications) + player.sendMessage(Text.of(TextColors.GRAY, "You have gained ", TextColors.GOLD, expAmount, TextColors.GRAY, " exp in the ", TextColors.GOLD, jobName, TextColors.GRAY, " job.")); + + try { + accountManager.getConfigManager().save(accountConfig); + } catch (IOException e) { + logger.warn("[TE] An error occurred while saving the account configuration file!"); + } + } + } + + /** + * Checks if the player has enough exp to level up. If they do they will gain a level and their current exp will be + * reset. + * + * @param player player object + */ + public void checkForLevel(Player player) { + UUID playerUUID = player.getUniqueId(); + String jobName = getPlayerJob(player); + int playerLevel = getJobLevel(jobName, player); + int playerCurExp = getJobExp(jobName, player); + int expToLevel = getExpToLevel(player); + + if (playerCurExp >= expToLevel) { + playerLevel += 1; + playerCurExp -= expToLevel; + + if (databaseActive) { + SQLQuery.builder(sqlHandler.dataSource) + .update("totaleconomy.levels") + .set(jobName) + .equals(String.valueOf(playerLevel)) + .where("uid") + .equals(playerUUID.toString()) + .build(); + + SQLQuery.builder(sqlHandler.dataSource) + .update("totaleconomy.experience") + .set(jobName) + .equals(String.valueOf(playerCurExp)) + .where("uid") + .equals(playerUUID.toString()) + .build(); + + // TODO: Handle any issues that arise whilst updating + } else { + accountConfig.getNode(playerUUID.toString(), "jobstats", jobName, "level").setValue(playerLevel); + accountConfig.getNode(playerUUID.toString(), "jobstats", jobName, "exp").setValue(playerCurExp); + } + + player.sendMessage(Text.of(TextColors.GRAY, "Congratulations, you are now a level ", TextColors.GOLD, + playerLevel, " ", titleize(jobName))); + } + } + + /** + * Checks the jobs config for the jobName. + * + * @param jobName name of the job + * @return boolean if the job exists or not + */ + public boolean jobExists(String jobName) { + if (jobsConfig.getNode(jobName.toLowerCase()).getValue() != null) { + return true; + } + + return false; + } + + /** + * Convert strings to titles (title -> Title) + * + * @param input the string to be titleized + * @return String the titileized version of the input + */ + public String titleize(String input) { + return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase(); + } + + /** + * Notifies a player when they are rewarded for completing a job action + * + * @param amount + */ + private void notifyPlayer(Player player, BigDecimal amount) { + Currency defaultCurrency = totalEconomy.getDefaultCurrency(); + + player.sendMessage(Text.of(TextColors.GOLD, defaultCurrency.format(amount, defaultCurrency.getDefaultFractionDigits()), TextColors.GRAY, " has been added to your balance.")); + } + + /** + * Set the users's job. + * + * @param user User object + * @param jobName name of the job + */ + public boolean setJob(User user, String jobName) { + UUID userUUID = user.getUniqueId(); + + // Just in case the job name was not passed in as lowercase, make it lowercase + jobName = jobName.toLowerCase(); + + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .update("totaleconomy.accounts") + .set("job") + .equals(jobName) + .where("uid") + .equals(userUUID.toString()) + .build(); + + if (sqlQuery.getRowsAffected() > 0) { + return true; + } else { + logger.warn("[TE] An error occurred while changing the job of " + user.getUniqueId() + "/" + user.getName() + "!"); + return false; + } + } else { + accountConfig.getNode(userUUID.toString(), "job").setValue(jobName); + + accountConfig.getNode(userUUID.toString(), "jobstats", jobName, "level").setValue( + accountConfig.getNode(userUUID.toString(), "jobstats", jobName, "level").getInt(1)); + + accountConfig.getNode(userUUID.toString(), "jobstats", jobName, "exp").setValue( + accountConfig.getNode(userUUID.toString(), "jobstats", jobName, "exp").getInt(0)); + + try { + accountManager.getConfigManager().save(accountConfig); + } catch (IOException e) { + logger.warn("[TE] An error occurred while changing the job of " + user.getUniqueId() + "/" + user.getName() + "!"); + } + + return true; + } + + } + + /** + * Get a jobSet by name + * + * @return {@link Optional} jobSet + */ + public Optional getJobSet(String name) { + return Optional.ofNullable(jobSets.getOrDefault(name, null)); + } + + /** + * Get the user's current job as a String for output + * + * @param user + * @return String the job the user currently has + */ + public String getPlayerJob(User user) { + UUID uuid = user.getUniqueId(); + + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .select("job") + .from("totaleconomy.accounts") + .where("uid") + .equals(uuid.toString()) + .build(); + + return sqlQuery.getString("unemployed").toLowerCase(); + } else { + return accountConfig.getNode(user.getUniqueId().toString(), "job").getString("unemployed").toLowerCase(); + } + } + + /** + * Get a TEJob object by a job name + * + * @param jobName Name of the job + * @param tryUnemployed Whether or not to try returning the unemployed job when the job wasn't found + * @return {@link TEJob} the job; {@code null} for not found + */ + public Optional getJob(String jobName, boolean tryUnemployed) { + TEJob job = jobsMap.getOrDefault(jobName, null); + + if (job != null || !tryUnemployed) + return Optional.ofNullable(job); + + return getJob("unemployed", false); + } + + /** + * Get the players level for the passed in job + * + * @param jobName the name of the job + * @param user the user object + * @return int the job level + */ + public int getJobLevel(String jobName, User user) { + UUID playerUUID = user.getUniqueId(); + + // Just in case the job name was not passed in as lowercase, make it lowercase + jobName = jobName.toLowerCase(); + + if (!jobName.equals("unemployed")) { + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .select(jobName) + .from("totaleconomy.levels") + .where("uid") + .equals(playerUUID.toString()) + .build(); + + return sqlQuery.getInt(1); + } else { + return accountConfig.getNode(user.getUniqueId().toString(), "jobstats", jobName, "level").getInt(1); + } + } + + return 1; + } + + /** + * Get the players exp for the passed in job. + * + * @param jobName the name of the job + * @param user the user object + * @return int the job exp + */ + public int getJobExp(String jobName, User user) { + UUID playerUUID = user.getUniqueId(); + + // Just in case the job name was not passed in as lowercase, make it lowercase + jobName = jobName.toLowerCase(); + + if (!jobName.equals("unemployed")) { + if (databaseActive) { + SQLQuery sqlQuery = SQLQuery.builder(sqlHandler.dataSource) + .select(jobName) + .from("totaleconomy.experience") + .where("uid") + .equals(playerUUID.toString()) + .build(); + + return sqlQuery.getInt(0); + } else { + return accountConfig.getNode(playerUUID.toString(), "jobstats", jobName, "exp").getInt(0); + } + } + + return 0; + } + + /** + * Get the exp required to level. + * + * @param user user object + * @return int the amount of exp needed to level + */ + public int getExpToLevel(User user) { + String jobName = getPlayerJob(user); + int playerLevel = getJobLevel(jobName, user); + + // TODO: Custom algorithm for this, set from config + return playerLevel * 100; + } + + /** + * Gets a comma separated string of all of the jobs currently in the jobs config. + * + * @return Text Comma separated string of jobs + */ + public Text getJobList() { + List texts = new ArrayList<>(); + + jobsMap.forEach((jobName, jobObject) -> texts.add(Text.of( + TextActions.runCommand("/job set " + jobName), + TextActions.showText(Text.of("Click to change job")), + jobName)) + ); + + return Text.joinWith(Text.of(", "), texts.toArray(new Text[texts.size()])); + } + + /** + * Getter for the jobSet configuration + * + * @return ConfigurationNode the jobSet configuration + */ + public ConfigurationNode getJobSetConfig() { + return jobSetsConfig; + } + + /** + * Getter for the jobs configuration + * + * @return ConfigurationNode the jobs configuration + */ + public ConfigurationNode getJobsConfig() { + return jobsConfig; + } + + /** + * Checks sign contents and converts it to a "Job Changing" sign if conditions are met + * + * @param event ChangeSignEvent + */ + @Listener + public void onJobSignCheck(ChangeSignEvent event) { + SignData data = event.getText(); + Text lineOne = data.lines().get(0); + Text lineTwo = data.lines().get(1); + String lineOnePlain = lineOne.toPlain(); + String lineTwoPlain = lineTwo.toPlain(); + + if (lineOnePlain.equals("[TEJobs]")) { + lineOne = lineOne.toBuilder().color(TextColors.GOLD).build(); + + String jobName = lineTwoPlain.toLowerCase(); + if (jobExists(lineTwoPlain)) { + lineTwo = Text.of(jobName).toBuilder().color(TextColors.GRAY).build(); + } else { + lineTwo = Text.of(jobName).toBuilder().color(TextColors.RED).build(); + } + + data.set(data.lines().set(0, lineOne)); + data.set(data.lines().set(1, lineTwo)); + data.set(data.lines().set(2, Text.of())); + data.set(data.lines().set(3, Text.of())); + } + } + + /** + * Called when a player clicks a sign. If the clicked sign is a "Job Changing" sign then the player's job will + * be changed on click. + * + * @param event InteractBlockEvent + */ + @Listener + public void onSignInteract(InteractBlockEvent event) { + if (event.getCause().first(Player.class).isPresent()) { + Player player = event.getCause().first(Player.class).get(); + + if (event.getTargetBlock().getLocation().isPresent()) { + Optional tileEntityOpt = event.getTargetBlock().getLocation().get().getTileEntity(); + + if (tileEntityOpt.isPresent()) { + TileEntity tileEntity = tileEntityOpt.get(); + + if (tileEntity instanceof Sign) { + Sign sign = (Sign) tileEntity; + Optional data = sign.getOrCreate(SignData.class); + + if (data.isPresent()) { + SignData signData = data.get(); + Text lineOneText = signData.lines().get(0); + Text lineTwoText = signData.lines().get(1); + String lineOne = lineOneText.toPlain(); + String lineTwo = lineTwoText.toPlain().toLowerCase(); + + if (lineOne.equals("[TEJobs]")) { + if (jobExists(lineTwo)) { + if (setJob(player, lineTwo)) { + player.sendMessage(Text.of(TextColors.GRAY, "Job changed to: ", TextColors.GOLD, lineTwo)); + } else { + player.sendMessage(Text.of(TextColors.RED, "[TE] Failed to set job. Contact your administrator.")); + } + } else { + player.sendMessage(Text.of(TextColors.RED, "[TE] Sorry, this job does not exist")); + } + } + } + } + } + } + } + } + + /** + * Used for the break option in jobs. Will check if the job has the break node and if it does it will check if the + * block that was broken is present in the config of the player's job. If it is, it will grab the job exp reward as + * well as the pay. + * + * @param event ChangeBlockEvent.Break + */ + @Listener + public void onPlayerBlockBreak(ChangeBlockEvent.Break event) { + if (event.getCause().first(Player.class).isPresent()) { + Player player = event.getCause().first(Player.class).get(); + UUID playerUUID = player.getUniqueId(); + + String playerJob = getPlayerJob(player); + Optional optPlayerJob = getJob(playerJob, true); + + BlockState state = event.getTransactions().get(0).getOriginal().getState(); + String blockName = state.getType().getName(); + Optional blockCreator = event.getTransactions().get(0).getOriginal().getCreator(); + + if (optPlayerJob.isPresent()) { + Optional reward = Optional.empty(); + List sets = optPlayerJob.get().getSets(); + + for (String s : sets) { + Optional optSet = getJobSet(s); + + if (!optSet.isPresent()) { + logger.warn("Job " + playerJob + " has the nonexistent set \"" + s + "\""); + continue; + } + + Optional currentReward = optSet.get().getRewardFor("break", blockName); + + // Use the one giving higher exp in case of duplicates (faster comparision than BD) + if (reward.isPresent() && currentReward.isPresent()) { + if (currentReward.get().getExpReward() > reward.get().getExpReward()) { + reward = currentReward; + } + } else { + reward = currentReward; + } + } + + if (reward.isPresent()) { + int expAmount = reward.get().getExpReward(); + BigDecimal payAmount = reward.get().getMoneyReward(); + Optional growthTrait = reward.get().getGrowthTrait(); + + // If there is a growth trait calculate a percentage to compensate only partly grown crops + if (growthTrait.isPresent()) { + Optional> optTrait = state.getTrait(growthTrait.get()); + + if (!optTrait.isPresent()) { + logger.warn("Job " + playerJob + " break \"" + blockName + "\" has trait entry that couldn't be found on the block."); + return; + } + + if (!Integer.class.isAssignableFrom(optTrait.get().getValueClass())) { + logger.warn("Job " + playerJob + " break \"" + blockName + "\" has trait entry that cannot be read as Integer."); + return; + } + + Optional optVal = state.getTraitValue((BlockTrait) optTrait.get()); + + if (!optVal.isPresent()) { + logger.warn("Job " + playerJob + " break \"" + blockName + "\" has trait entry that couldn't be read as Integer."); + return; + } + + // Calculate percentages + Integer val = optVal.get(); + Collection optValues = (Collection) optTrait.get().getPossibleValues(); + Integer max = optValues.stream().max(Comparator.comparingInt(Integer::intValue)).orElse(0); + Integer min = optValues.stream().min(Comparator.comparingInt(Integer::intValue)).orElse(0); + double perc = (double) (val - min) / (double) (max - min); + payAmount = payAmount.multiply(BigDecimal.valueOf(perc)); + expAmount = (int) (expAmount * perc); + } else if (blockCreator.isPresent()) { + // A player placed the block and it doesn't indicate growth -> Do not pay to prevent exploits + return; + } + + boolean notify = accountManager.getJobNotificationState(player); + TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); + + if (notify) { + notifyPlayer(player, payAmount); + } + + addExp(player, expAmount); + playerAccount.deposit(totalEconomy.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + checkForLevel(player); + } + } + } + } + + /** + * Used for the place option in jobs. Will check if the job has the place node and if it does it will check if the + * block that was placed is present in the config of the player's job. If it is, it will grab the job exp reward as + * well as the pay. + * + * @param event ChangeBlockEvent.Place + */ + @Listener + public void onPlayerPlaceBlock(ChangeBlockEvent.Place event) { + if (event.getCause().first(Player.class).isPresent()) { + Player player = event.getCause().first(Player.class).get(); + UUID playerUUID = player.getUniqueId(); + + String playerJob = getPlayerJob(player); + Optional optPlayerJob = getJob(playerJob, true); + + String blockName = event.getTransactions().get(0).getFinal().getState().getType().getName(); + + if (optPlayerJob.isPresent()) { + Optional reward = Optional.empty(); + List sets = optPlayerJob.get().getSets(); + + for (String s : sets) { + Optional optSet = getJobSet(s); + + if (!optSet.isPresent()) { + logger.warn("Job " + playerJob + " has the nonexistent set \"" + s + "\""); + continue; + } + + Optional currentReward = optSet.get().getRewardFor("place", blockName); + + // Use the one giving higher exp in case of duplicates (faster comparision than BD) + if (reward.isPresent() && currentReward.isPresent()) { + if (currentReward.get().getExpReward() > reward.get().getExpReward()) { + reward = currentReward; + } + } else { + reward = currentReward; + } + } + + if (reward.isPresent()) { + int expAmount = reward.get().getExpReward(); + BigDecimal payAmount = reward.get().getMoneyReward(); + boolean notify = accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean(); + + TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); + + if (notify) { + notifyPlayer(player, payAmount); + } + + addExp(player, expAmount); + playerAccount.deposit(totalEconomy.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + checkForLevel(player); + } + } + } + } + + /** + * Used for the break option in jobs. Will check if the job has the break node and if it does it will check if the + * block that was broken is present in the config of the player's job. If it is, it will grab the job exp reward as + * well as the pay. + * + * @param event DestructEntityEvent.Death + */ + @Listener + public void onPlayerKillEntity(DestructEntityEvent.Death event) { + Optional optDamageSource = event.getCause().first(EntityDamageSource.class); + + if (optDamageSource.isPresent()) { + EntityDamageSource damageSource = optDamageSource.get(); + Entity killer = damageSource.getSource(); + Entity victim = event.getTargetEntity(); + + if (!(killer instanceof Player)) { + // If a projectile was shot to kill an entity, this will grab the player who shot it + Optional damageCreator = damageSource.getSource().getCreator(); + + if (damageCreator.isPresent()) + killer = Sponge.getServer().getPlayer(damageCreator.get()).get(); + } + + if (killer instanceof Player) { + Player player = (Player) killer; + UUID playerUUID = player.getUniqueId(); + String victimName = victim.getType().getName(); + + String playerJob = getPlayerJob(player); + Optional optPlayerJob = getJob(playerJob, true); + + if (optPlayerJob.isPresent()) { + Optional reward = Optional.empty(); + List sets = optPlayerJob.get().getSets(); + + for (String s : sets) { + Optional optSet = getJobSet(s); + + if (!optSet.isPresent()) { + logger.warn("Job " + playerJob + " has the nonexistent set \"" + s + "\""); + continue; + } + + Optional currentReward = optSet.get().getRewardFor("kill", victimName); + + // Use the one giving higher exp in case of duplicates (faster comparision than BD) + if (reward.isPresent() && currentReward.isPresent()) { + if (currentReward.get().getExpReward() > reward.get().getExpReward()) { + reward = currentReward; + } + } else { + reward = currentReward; + } + } + + if (reward.isPresent()) { + int expAmount = reward.get().getExpReward(); + BigDecimal payAmount = reward.get().getMoneyReward(); + boolean notify = accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean(); + + TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); + + if (notify) { + notifyPlayer(player, payAmount); + } + + addExp(player, expAmount); + playerAccount.deposit(totalEconomy.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + checkForLevel(player); + } + } + } + } + } + + /** + * Used for the catch option in jobs. Will check if the job has the catch node and if it does it will check if the + * item that was caught is present in the config of the player's job. If it is, it will grab the job exp reward as + * well as the pay. + * + * @param event FishingEvent.Stop + */ + @Listener + public void onPlayerFish(FishingEvent.Stop event) { + if (event.getCause().first(Player.class).isPresent()) { + // no transaction, so execution can stop + if (event.getItemStackTransaction().size() == 0) { + return; + } + + Transaction itemTransaction = event.getItemStackTransaction().get(0); + ItemStack itemStack = itemTransaction.getFinal().createStack(); + Player player = event.getCause().first(Player.class).get(); + UUID playerUUID = player.getUniqueId(); + + String playerJob = getPlayerJob(player); + Optional optPlayerJob = getJob(playerJob, true); + + if (optPlayerJob.isPresent()) { + if (itemStack.get(FishData.class).isPresent()) { + FishData fishData = itemStack.get(FishData.class).get(); + String fishName = fishData.type().get().getName(); + + Optional reward = Optional.empty(); + List sets = optPlayerJob.get().getSets(); + + for (String s : sets) { + Optional optSet = getJobSet(s); + + if (!optSet.isPresent()) { + logger.warn("Job " + playerJob + " has the nonexistent set \"" + s + "\""); + continue; + } + + Optional currentReward = optSet.get().getRewardFor("catch", fishName); + + // Use the one giving higher exp in case of duplicates (faster comparision than BD) + if (reward.isPresent() && currentReward.isPresent()) { + if (currentReward.get().getExpReward() > reward.get().getExpReward()) { + reward = currentReward; + } + } else { + reward = currentReward; + } + } + + if (reward.isPresent()) { + int expAmount = reward.get().getExpReward(); + BigDecimal payAmount = reward.get().getMoneyReward(); + boolean notify = accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean(); + + TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); + + if (notify) { + notifyPlayer(player, payAmount); + } + + addExp(player, expAmount); + playerAccount.deposit(totalEconomy.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); + checkForLevel(player); + } + } + } + } + } +} diff --git a/src/main/java/com/erigitic/jobs/TEJobSet.java b/src/main/java/com/erigitic/jobs/TEJobSet.java new file mode 100644 index 00000000..780d237f --- /dev/null +++ b/src/main/java/com/erigitic/jobs/TEJobSet.java @@ -0,0 +1,67 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs; + +import ninja.leaping.configurate.ConfigurationNode; + +import java.util.*; + +public class TEJobSet { + private List actionRewards = new ArrayList(); + + public TEJobSet(ConfigurationNode node) { + node.getChildrenMap().forEach((action, targetNode) -> { + if ((action instanceof String) && targetNode != null) { + targetNode.getChildrenMap().forEach((targetID, rewardsNode) -> { + if ((targetID instanceof String) && rewardsNode != null) { + TEActionReward actionReward = new TEActionReward( + (String) action, + (String) targetID, + rewardsNode.getNode("exp").getString("0"), + rewardsNode.getNode("money").getString("0"), + rewardsNode.getNode("growthTrait").getString(null) + ); + + if (actionReward.isValid()) { + actionRewards.add(actionReward); + } + } + }); + } + }); + } + + public Optional getRewardFor(String action, String targetID) { + return actionRewards.stream() + .filter(teActionReward -> teActionReward.getAction().equals(action)) + .filter(teActionReward -> teActionReward.getTargetID().equals(targetID)) + .findFirst(); + } + + public List getActionRewards() { + return actionRewards; + } +} diff --git a/src/main/java/com/erigitic/jobs/TEJobs.java b/src/main/java/com/erigitic/jobs/TEJobs.java deleted file mode 100644 index 5e2c38c1..00000000 --- a/src/main/java/com/erigitic/jobs/TEJobs.java +++ /dev/null @@ -1,588 +0,0 @@ -/* - * This file is part of Total Economy, licensed under the MIT License (MIT). - * - * Copyright (c) Eric Grandt - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.erigitic.jobs; - -import com.erigitic.config.AccountManager; -import com.erigitic.config.TEAccount; -import com.erigitic.jobs.jobs.FishermanJob; -import com.erigitic.jobs.jobs.LumberjackJob; -import com.erigitic.jobs.jobs.MinerJob; -import com.erigitic.jobs.jobs.WarriorJob; -import com.erigitic.main.TotalEconomy; -import ninja.leaping.configurate.ConfigurationNode; -import ninja.leaping.configurate.commented.CommentedConfigurationNode; -import ninja.leaping.configurate.hocon.HoconConfigurationLoader; -import ninja.leaping.configurate.loader.ConfigurationLoader; -import org.slf4j.Logger; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.block.tileentity.Sign; -import org.spongepowered.api.block.tileentity.TileEntity; -import org.spongepowered.api.data.Transaction; -import org.spongepowered.api.data.manipulator.mutable.item.FishData; -import org.spongepowered.api.data.manipulator.mutable.tileentity.SignData; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.action.FishingEvent; -import org.spongepowered.api.event.block.ChangeBlockEvent; -import org.spongepowered.api.event.block.InteractBlockEvent; -import org.spongepowered.api.event.block.tileentity.ChangeSignEvent; -import org.spongepowered.api.event.cause.Cause; -import org.spongepowered.api.event.cause.NamedCause; -import org.spongepowered.api.event.cause.entity.damage.source.EntityDamageSource; -import org.spongepowered.api.event.entity.DestructEntityEvent; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.scheduler.Scheduler; -import org.spongepowered.api.scheduler.Task; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.format.TextColors; - -import java.io.File; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.*; -import java.util.concurrent.TimeUnit; - -public class TEJobs { - private TotalEconomy totalEconomy; - private AccountManager accountManager; - private ConfigurationNode accountConfig; - private Logger logger; - - private File jobsFile; - private ConfigurationLoader loader; - private ConfigurationNode jobsConfig; - - private MinerJob miner; - private LumberjackJob lumberjack; - private WarriorJob warrior; - private FishermanJob fisherman; - - public TEJobs(TotalEconomy totalEconomy) { - this.totalEconomy = totalEconomy; - - miner = new MinerJob(); - lumberjack = new LumberjackJob(); - warrior = new WarriorJob(); - fisherman = new FishermanJob(); - - accountManager = totalEconomy.getAccountManager(); - accountConfig = accountManager.getAccountConfig(); - logger = totalEconomy.getLogger(); - - setupConfig(); - - if (totalEconomy.isLoadSalary()) - startSalaryTask(); - } - - /** - * Start the timer that pays out the salary to each player after a specified time in seconds - */ - private void startSalaryTask() { - Scheduler scheduler = totalEconomy.getGame().getScheduler(); - Task.Builder payTask = scheduler.createTaskBuilder(); - - payTask.execute(() -> { - for (Player player : totalEconomy.getServer().getOnlinePlayers()) { - BigDecimal salary = new BigDecimal(jobsConfig.getNode(getPlayerJob(player), "salary").getString()); - boolean salaryDisabled = jobsConfig.getNode(getPlayerJob(player), "disablesalary").getBoolean(); - - if (!salaryDisabled) { - TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); - - playerAccount.deposit(totalEconomy.getDefaultCurrency(), salary, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); - player.sendMessage(Text.of(TextColors.GRAY, "Your salary of ", TextColors.GOLD, - totalEconomy.getCurrencySymbol(), salary, TextColors.GRAY, " has just been paid.")); - } - } - }).delay(jobsConfig.getNode("salarydelay").getInt(), TimeUnit.SECONDS).interval(jobsConfig.getNode("salarydelay") - .getInt(), TimeUnit.SECONDS).name("Pay Day").submit(totalEconomy); - } - - /** - * Setup the jobs config - */ - public void setupConfig() { - jobsFile = new File(totalEconomy.getConfigDir(), "jobs.conf"); - loader = HoconConfigurationLoader.builder().setFile(jobsFile).build(); - - try { - jobsConfig = loader.load(); - - if (!jobsFile.exists()) { - jobsConfig.getNode("jobs").setValue("Miner, Lumberjack, Warrior, Fisherman"); - - miner.setupJobValues(jobsConfig); - lumberjack.setupJobValues(jobsConfig); - warrior.setupJobValues(jobsConfig); - fisherman.setupJobValues(jobsConfig); - - jobsConfig.getNode("Unemployed", "disablesalary").setValue(false); - jobsConfig.getNode("Unemployed", "salary").setValue(20); - jobsConfig.getNode("salarydelay").setValue(300); - - loader.save(jobsConfig); - } - } catch (IOException e) { - logger.warn("Could not create jobs config file!"); - } - } - - /** - * Reload the jobs config - */ - public void reloadConfig() { - try { - jobsConfig = loader.load(); - logger.info("Reloading jobs configuration file."); - } catch (IOException e) { - logger.warn("Could not reload jobs configuration file!"); - } - } - - /** - * Add exp to player's current job - * - * @param player player object - * @param expAmount amount of exp to be gained - */ - public void addExp(Player player, int expAmount) { - String jobName = getPlayerJob(player); - UUID playerUUID = player.getUniqueId(); - int curExp = accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Exp").getInt(); - - accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Exp").setValue(curExp + expAmount); - - try { - accountManager.getConfigManager().save(accountConfig); - } catch (IOException e) { - logger.warn("Problem saving account config!"); - } - - if (accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean() == true) - player.sendMessage(Text.of(TextColors.GRAY, "You have gained ", TextColors.GOLD, expAmount, TextColors.GRAY, - " exp in the ", TextColors.GOLD, jobName, TextColors.GRAY, " job.")); - } - - /** - * Checks if the player has enough exp to level up. If they do they will gain a level and their current exp will be - * reset. - * - * @param player player object - */ - public void checkForLevel(Player player) { - UUID playerUUID = player.getUniqueId(); - String jobName = getPlayerJob(player); - int playerLevel = accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Level").getInt(); - int playerCurExp = accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Exp").getInt(); - int expToLevel = getExpToLevel(player); - - if (playerCurExp >= expToLevel) { - accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Level").setValue(playerLevel + 1); - accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Exp").setValue(playerCurExp - expToLevel); - player.sendMessage(Text.of(TextColors.GRAY, "Congratulations, you are now a level ", TextColors.GOLD, - playerLevel + 1, " ", jobName, ".")); - } - } - - /** - * Checks the jobs config for the jobName. - * - * @param jobName name of the job - * @return boolean if the job exists or not - */ - public boolean jobExists(String jobName) { - if (jobsConfig.getNode(convertToTitle(jobName)).getValue() != null) { - return true; - } - - return false; - } - - /** - * Convert strings to titles (title -> Title) - * - * @param input the string to be titleized - * @return String the titileized version of the input - */ - public String convertToTitle(String input) { - return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase(); - } - - /** - * Set the player's job. - * - * @param player Player object - * @param jobName name of the job - */ - public void setJob(Player player, String jobName) { - UUID playerUUID = player.getUniqueId(); - boolean jobPermissions = totalEconomy.isJobPermissions(); - - if (jobExists(jobName)) { - if ((jobPermissions && player.hasPermission("totaleconomy.job." + jobName)) || !jobPermissions) { - jobName = convertToTitle(jobName); - - accountConfig.getNode(playerUUID.toString(), "job").setValue(jobName); - - if (accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Level").getValue() == null) { - accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Level").setValue(1); - } - - if (accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Exp").getValue() == null) { - accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Exp").setValue(0); - } - - try { - accountManager.getConfigManager().save(accountConfig); - } catch (IOException e) { - logger.warn("Could not save account config while setting job!"); - } - - player.sendMessage(Text.of(TextColors.GRAY, "Your job has been changed to ", TextColors.GOLD, jobName)); - } else { - player.sendMessage(Text.of(TextColors.RED, "You do not have permission to become this job.")); - } - } else { - player.sendMessage(Text.of(TextColors.RED, "[TEJobs] This job does not exist")); - } - } - - /** - * Get the player's current job - * - * @param player - * @return String the job the player currently has - */ - public String getPlayerJob(Player player) { - return accountConfig.getNode(player.getUniqueId().toString(), "job").getString(); - } - - /** - * Get the players exp for the passed in job. - * - * @param jobName the name of the job - * @param player the player object - * @return int the job exp - */ - public int getJobExp(String jobName, Player player) { - return accountConfig.getNode(player.getUniqueId().toString(), "jobstats", jobName + "Exp").getInt(); - } - - /** - * Get the players level for the passed in job - * - * @param jobName the name of the job - * @param player the player object - * @return int the job level - */ - public int getJobLevel(String jobName, Player player) { - return accountConfig.getNode(player.getUniqueId().toString(), "jobstats", jobName + "Level").getInt(); - } - - /** - * Get the exp required to level. - * - * @param player player object - * @return int the amount of exp needed to level - */ - public int getExpToLevel(Player player) { - UUID playerUUID = player.getUniqueId(); - String jobName = getPlayerJob(player); - int playerLevel = accountConfig.getNode(playerUUID.toString(), "jobstats", jobName + "Level").getInt(); - - return playerLevel * 100; - } - - /** - * Gets a list of all of the jobs currently in the jobs config. - * - * @return String list of jobs - */ - public String getJobList() { - return jobsConfig.getNode("jobs").getString(); - } - - /** - * Getter for the jobs configuration - * - * @return ConfigurationNode the jobs configuration - */ - public ConfigurationNode getJobsConfig() { - return jobsConfig; - } - - /** - * Checks sign contents and converts it to a "Job Changing" sign if conditions are met - * - * @param event ChangeSignEvent - */ - @Listener - public void onJobSignCheck(ChangeSignEvent event) { - SignData data = event.getText(); - Text lineOne = data.lines().get(0); - Text lineTwo = data.lines().get(1); - String lineOnePlain = lineOne.toPlain(); - String lineTwoPlain = lineTwo.toPlain(); - - if (lineOnePlain.equals("[TEJobs]")) { - lineOne = lineOne.toBuilder().color(TextColors.GOLD).build(); - - String jobName = convertToTitle(lineTwoPlain); - - if (jobExists(lineTwoPlain)) { - lineTwo = Text.of(jobName).toBuilder().color(TextColors.GRAY).build(); - } else { - lineTwo = Text.of(jobName).toBuilder().color(TextColors.RED).build(); - } - - data.set(data.lines().set(0, lineOne)); - data.set(data.lines().set(1, lineTwo)); - data.set(data.lines().set(2, Text.of())); - data.set(data.lines().set(3, Text.of())); - } - } - - /** - * Called when a player clicks a sign. If the clicked sign is a "Job Changing" sign then the player's job will - * be changed on click. - * - * @param event InteractBlockEvent - */ - @Listener - public void onSignInteract(InteractBlockEvent event) { - if (event.getCause().first(Player.class).isPresent()) { - Player player = event.getCause().first(Player.class).get(); - - if (event.getTargetBlock().getLocation().isPresent()) { - Optional tileEntityOpt = event.getTargetBlock().getLocation().get().getTileEntity(); - - if (tileEntityOpt.isPresent()) { - TileEntity tileEntity = tileEntityOpt.get(); - - if (tileEntity instanceof Sign) { - Sign sign = (Sign) tileEntity; - Optional data = sign.getOrCreate(SignData.class); - - if (data.isPresent()) { - SignData signData = data.get(); - Text lineOneText = signData.lines().get(0); - Text lineTwoText = signData.lines().get(1); - String lineOne = lineOneText.toPlain(); - String lineTwo = lineTwoText.toPlain(); - - if (lineOne.equals("[TEJobs]") && jobExists(lineTwo)) { - setJob(player, lineTwo); - } - } - } - } - } - } - } - - /** - * Used for the break option in jobs. Will check if the job has the break node and if it does it will check if the - * block that was broken is present in the config of the player's job. If it is, it will grab the job exp reward as - * well as the pay. - * - * @param event ChangeBlockEvent.Break - */ - @Listener - public void onPlayerBlockBreak(ChangeBlockEvent.Break event) { - if (event.getCause().first(Player.class).isPresent()) { - Player player = event.getCause().first(Player.class).get(); - UUID playerUUID = player.getUniqueId(); - String playerJob = getPlayerJob(player); - String blockName = event.getTransactions().get(0).getOriginal().getState().getType().getName(); - Optional blockCreator = event.getTransactions().get(0).getOriginal().getCreator(); - - // Checks if the users current job has the break node. - boolean hasBreakNode = (jobsConfig.getNode(playerJob, "break").getValue() != null); - - if (jobsConfig.getNode(playerJob).getValue() != null) { - if (hasBreakNode && jobsConfig.getNode(playerJob, "break", blockName).getValue() != null) { - if (!blockCreator.isPresent()) { - int expAmount = jobsConfig.getNode(playerJob, "break", blockName, "expreward").getInt(); - boolean notify = accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean(); - - BigDecimal payAmount = new BigDecimal(jobsConfig.getNode(playerJob, "break", blockName, "pay").getString()).setScale(2, BigDecimal.ROUND_DOWN); - - TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); - - if (notify) { - player.sendMessage(Text.of(TextColors.GOLD, accountManager.getDefaultCurrency().getSymbol(), payAmount, TextColors.GRAY, " has been added to your balance.")); - } - - addExp(player, expAmount); - playerAccount.deposit(accountManager.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); - checkForLevel(player); - } - } - } - } - } - - /** - * Used for the place option in jobs. Will check if the job has the place node and if it does it will check if the - * block that was placed is present in the config of the player's job. If it is, it will grab the job exp reward as - * well as the pay. - * - * @param event ChangeBlockEvent.Place - */ - @Listener - public void onPlayerPlaceBlock(ChangeBlockEvent.Place event) { - if (event.getCause().first(Player.class).isPresent()) { - Player player = event.getCause().first(Player.class).get(); - UUID playerUUID = player.getUniqueId(); - String playerJob = getPlayerJob(player); - String blockName = event.getTransactions().get(0).getFinal().getState().getType().getName(); - - //Checks if the users current job has the place node. - boolean hasPlaceNode = (jobsConfig.getNode(playerJob, "place").getValue() != null); - - if (jobsConfig.getNode(playerJob).getValue() != null) { - if (hasPlaceNode && jobsConfig.getNode(playerJob, "place", blockName).getValue() != null) { - int expAmount = jobsConfig.getNode(playerJob, "place", blockName, "expreward").getInt(); - boolean notify = accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean(); - - BigDecimal payAmount = new BigDecimal(jobsConfig.getNode(playerJob, "place", blockName, "pay").getString()).setScale(2, BigDecimal.ROUND_DOWN); - - TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); - - if (notify) { - player.sendMessage(Text.of(TextColors.GOLD, accountManager.getDefaultCurrency().getSymbol(), payAmount, TextColors.GRAY, " has been added to your balance.")); - } - - addExp(player, expAmount); - playerAccount.deposit(accountManager.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); - checkForLevel(player); - } - } - } - } - - /** - * Used for the break option in jobs. Will check if the job has the break node and if it does it will check if the - * block that was broken is present in the config of the player's job. If it is, it will grab the job exp reward as - * well as the pay. - * - * @param event DestructEntityEvent.Death - */ - @Listener - public void onPlayerKillEntity(DestructEntityEvent.Death event) { - Optional optDamageSource = event.getCause().first(EntityDamageSource.class); - - if (optDamageSource.isPresent()) { - EntityDamageSource damageSource = optDamageSource.get(); - Entity killer = damageSource.getSource(); - Entity victim = event.getTargetEntity(); - - if (!(killer instanceof Player)) { - // If a projectile was shot to kill an entity, this will grab the player who shot it - Optional damageCreator = damageSource.getSource().getCreator(); - - if (damageCreator.isPresent()) - killer = Sponge.getServer().getPlayer(damageCreator.get()).get(); - } - - if (killer instanceof Player) { - Player player = (Player) killer; - UUID playerUUID = player.getUniqueId(); - String playerJob = getPlayerJob(player); - String victimName = victim.getType().getName(); - - boolean hasKillNode = (jobsConfig.getNode(playerJob, "kill").getValue() != null); - - if (jobsConfig.getNode(playerJob).getValue() != null) { - if (hasKillNode && jobsConfig.getNode(playerJob, "kill", victimName).getValue() != null) { - int expAmount = jobsConfig.getNode(playerJob, "kill", victimName, "expreward").getInt(); - boolean notify = accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean(); - - BigDecimal payAmount = new BigDecimal(jobsConfig.getNode(playerJob, "kill", victimName, "pay").getString()).setScale(2, BigDecimal.ROUND_DOWN); - - TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); - - if (notify) { - player.sendMessage(Text.of(TextColors.GOLD, accountManager.getDefaultCurrency().getSymbol(), payAmount, TextColors.GRAY, " has been added to your balance.")); - } - - addExp(player, expAmount); - playerAccount.deposit(accountManager.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); - checkForLevel(player); - } - } - } - } - } - - /** - * Used for the catch option in jobs. Will check if the job has the catch node and if it does it will check if the - * item that was caught is present in the config of the player's job. If it is, it will grab the job exp reward as - * well as the pay. - * - * @param event FishingEvent.Stop - */ - @Listener - public void onPlayerFish(FishingEvent.Stop event) { - if (event.getCause().first(Player.class).isPresent()) { - Transaction itemTransaction = event.getItemStackTransaction().get(0); - ItemStack itemStack = itemTransaction.getFinal().createStack(); - Player player = event.getCause().first(Player.class).get(); - UUID playerUUID = player.getUniqueId(); - String playerJob = getPlayerJob(player); - - boolean hasCatchNode = (jobsConfig.getNode(playerJob, "catch").getValue() != null); - - if (itemStack.get(FishData.class).isPresent()) { - if (jobsConfig.getNode(playerJob).getValue() != null) { - FishData fishData = itemStack.get(FishData.class).get(); - String fishName = fishData.type().get().getName(); - - if (hasCatchNode && jobsConfig.getNode(playerJob, "catch", fishName).getValue() != null) { - int expAmount = jobsConfig.getNode(playerJob, "catch", fishName, "expreward").getInt(); - boolean notify = accountConfig.getNode(playerUUID.toString(), "jobnotifications").getBoolean(); - - BigDecimal payAmount = new BigDecimal(jobsConfig.getNode(playerJob, "catch", fishName, "pay").getString()).setScale(2, BigDecimal.ROUND_DOWN); - - TEAccount playerAccount = (TEAccount) accountManager.getOrCreateAccount(player.getUniqueId()).get(); - - if (notify) { - player.sendMessage(Text.of(TextColors.GOLD, accountManager.getDefaultCurrency().getSymbol(), payAmount, TextColors.GRAY, " has been added to your balance.")); - } - - addExp(player, expAmount); - playerAccount.deposit(accountManager.getDefaultCurrency(), payAmount, Cause.of(NamedCause.of("TotalEconomy", totalEconomy.getPluginContainer()))); - checkForLevel(player); - } - } - - } - } - } -} \ No newline at end of file diff --git a/src/main/java/com/erigitic/jobs/jobs/FarmerJob.java b/src/main/java/com/erigitic/jobs/jobs/FarmerJob.java new file mode 100644 index 00000000..778397e5 --- /dev/null +++ b/src/main/java/com/erigitic/jobs/jobs/FarmerJob.java @@ -0,0 +1,56 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs.jobs; + +import com.erigitic.jobs.JobBasedRequirement; +import ninja.leaping.configurate.ConfigurationNode; + +import java.util.Arrays; + +public class FarmerJob implements Job { + + private final String NAME = "farmer"; + private final String[] SETS = { "crops" }; + + @Override + public String getName() { + return NAME; + } + + @Override + public String[] getSets() { + return SETS; + } + + @Override + public void populateNode(ConfigurationNode node) { + node = node.getNode(NAME); + + node.getNode("salary").setValue(20); + node.getNode("sets").setValue(Arrays.asList(SETS)); + new JobBasedRequirement(null, 0, "totaleconomy.job.farmer").addTo(node); + } +} diff --git a/src/main/java/com/erigitic/jobs/jobs/FishermanJob.java b/src/main/java/com/erigitic/jobs/jobs/FishermanJob.java index 59e65a84..a065c9e4 100644 --- a/src/main/java/com/erigitic/jobs/jobs/FishermanJob.java +++ b/src/main/java/com/erigitic/jobs/jobs/FishermanJob.java @@ -25,20 +25,32 @@ package com.erigitic.jobs.jobs; -import com.erigitic.jobs.Job; +import com.erigitic.jobs.JobBasedRequirement; import ninja.leaping.configurate.ConfigurationNode; +import java.util.Arrays; + public class FishermanJob implements Job { - public void setupJobValues(ConfigurationNode jobsConfig) { - String[][] catchValues = {{"cod", "25", "50.00"}, {"salmon", "100", "150.00"}, {"pufferfish", "250", "300.00"}}; - for (int i = 0; i < catchValues.length; i++) { - jobsConfig.getNode("Fisherman", "catch", catchValues[i][0], "expreward").setValue(catchValues[i][1]); - jobsConfig.getNode("Fisherman", "catch", catchValues[i][0], "pay").setValue(catchValues[i][2]); - } + private final String NAME = "fisherman"; + private final String[] SETS = { "fish" }; + + @Override + public String getName() { + return NAME; + } + + @Override + public String[] getSets() { + return SETS; + } + + @Override + public void populateNode(ConfigurationNode node) { + node = node.getNode(NAME); - jobsConfig.getNode("Fisherman", "disablesalary").setValue(false); - jobsConfig.getNode("Fisherman", "salary").setValue(20); - jobsConfig.getNode("Fisherman", "permission").setValue("totaleconomy.job.fisherman"); + node.getNode("salary").setValue(20); + node.getNode("sets").setValue(Arrays.asList(SETS)); + new JobBasedRequirement(null, 0, "totaleconomy.job.fisherman").addTo(node); } } diff --git a/src/main/java/com/erigitic/jobs/Job.java b/src/main/java/com/erigitic/jobs/jobs/Job.java similarity index 91% rename from src/main/java/com/erigitic/jobs/Job.java rename to src/main/java/com/erigitic/jobs/jobs/Job.java index c8e7a120..41338774 100644 --- a/src/main/java/com/erigitic/jobs/Job.java +++ b/src/main/java/com/erigitic/jobs/jobs/Job.java @@ -23,10 +23,15 @@ * SOFTWARE. */ -package com.erigitic.jobs; +package com.erigitic.jobs.jobs; import ninja.leaping.configurate.ConfigurationNode; public interface Job { - void setupJobValues(ConfigurationNode jobsConfig); + + String getName(); + + String[] getSets(); + + void populateNode(ConfigurationNode node); } diff --git a/src/main/java/com/erigitic/jobs/jobs/LumberjackJob.java b/src/main/java/com/erigitic/jobs/jobs/LumberjackJob.java index 3888a941..8f32f201 100644 --- a/src/main/java/com/erigitic/jobs/jobs/LumberjackJob.java +++ b/src/main/java/com/erigitic/jobs/jobs/LumberjackJob.java @@ -25,25 +25,32 @@ package com.erigitic.jobs.jobs; -import com.erigitic.jobs.Job; +import com.erigitic.jobs.JobBasedRequirement; import ninja.leaping.configurate.ConfigurationNode; +import java.util.Arrays; + public class LumberjackJob implements Job { - public void setupJobValues(ConfigurationNode jobsConfig) { - String[][] breakValues = {{"minecraft:log", "10", "1.00"}, {"minecraft:leaves", "1", "0.01"}}; - String[][] placeValue = {{"minecraft:sapling", "1", "0.10"}}; - - for (int i = 0; i < breakValues.length; i++) { - jobsConfig.getNode("Lumberjack", "break", breakValues[i][0], "expreward").setValue(breakValues[i][1]); - jobsConfig.getNode("Lumberjack", "break", breakValues[i][0], "pay").setValue(breakValues[i][2]); - } - - for (int i = 0; i < placeValue.length; i++) { - jobsConfig.getNode("Lumberjack", "place", placeValue[i][0], "expreward").setValue(placeValue[i][1]); - jobsConfig.getNode("Lumberjack", "place", placeValue[i][0], "pay").setValue(placeValue[i][2]); - } - jobsConfig.getNode("Lumberjack", "disablesalary").setValue(false); - jobsConfig.getNode("Lumberjack", "salary").setValue(20); - jobsConfig.getNode("Lumberjack", "permission").setValue("totaleconomy.job.lumberjack"); + + private final String NAME = "lumberjack"; + private final String[] SETS = { NAME + "Set" }; + + @Override + public String getName() { + return NAME; + } + + @Override + public String[] getSets() { + return SETS; + } + + @Override + public void populateNode(ConfigurationNode node) { + node = node.getNode(NAME); + + node.getNode("salary").setValue(20); + node.getNode("sets").setValue(Arrays.asList(SETS)); + new JobBasedRequirement(null, 0, "totaleconomy.job.lumberjack").addTo(node); } } \ No newline at end of file diff --git a/src/main/java/com/erigitic/jobs/jobs/MinerJob.java b/src/main/java/com/erigitic/jobs/jobs/MinerJob.java index 2ae70939..2a7c81cc 100644 --- a/src/main/java/com/erigitic/jobs/jobs/MinerJob.java +++ b/src/main/java/com/erigitic/jobs/jobs/MinerJob.java @@ -25,21 +25,32 @@ package com.erigitic.jobs.jobs; -import com.erigitic.jobs.Job; +import com.erigitic.jobs.JobBasedRequirement; import ninja.leaping.configurate.ConfigurationNode; +import java.util.Arrays; + public class MinerJob implements Job { - public void setupJobValues(ConfigurationNode jobsConfig) { - String[][] breakValues = {{"minecraft:coal_ore", "5", "0.25"}, {"minecraft:iron_ore", "10", "0.50"}, {"minecraft:lapis_ore", "20", "4.00"}, - {"minecraft:gold_ore", "40", "5.00"}, {"minecraft:diamond_ore", "100", "25.00"}, {"minecraft:redstone_ore", "25", "2.00"}, - {"minecraft:emerald_ore", "50", "12.50"}, {"minecraft:quartz_ore", "5", "0.15"}}; - - for (int i = 0; i < breakValues.length; i++) { - jobsConfig.getNode("Miner", "break", breakValues[i][0], "expreward").setValue(breakValues[i][1]); - jobsConfig.getNode("Miner", "break", breakValues[i][0], "pay").setValue(breakValues[i][2]); - } - jobsConfig.getNode("Miner", "disablesalary").setValue(false); - jobsConfig.getNode("Miner", "salary").setValue(20); - jobsConfig.getNode("Miner", "permission").setValue("totaleconomy.job.miner"); + + private final String NAME = "miner"; + private final String[] SETS = { "ores" }; + + @Override + public String getName() { + return NAME; + } + + @Override + public String[] getSets() { + return SETS; + } + + @Override + public void populateNode(ConfigurationNode node) { + node = node.getNode(NAME); + + node.getNode("salary").setValue(20); + node.getNode("sets").setValue(Arrays.asList(SETS)); + new JobBasedRequirement(null, 0, "totaleconomy.job.miner").addTo(node); } } diff --git a/src/main/java/com/erigitic/jobs/jobs/UnemployedJob.java b/src/main/java/com/erigitic/jobs/jobs/UnemployedJob.java new file mode 100644 index 00000000..488793ed --- /dev/null +++ b/src/main/java/com/erigitic/jobs/jobs/UnemployedJob.java @@ -0,0 +1,50 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs.jobs; + +import ninja.leaping.configurate.ConfigurationNode; + +public class UnemployedJob implements Job { + + private final String NAME = "unemployed"; + + @Override + public String getName() { + return NAME; + } + + @Override + public String[] getSets() { + return null; + } + + @Override + public void populateNode(ConfigurationNode node) { + node = node.getNode(NAME); + + node.getNode("salary").setValue(20); + } +} diff --git a/src/main/java/com/erigitic/jobs/jobs/WarriorJob.java b/src/main/java/com/erigitic/jobs/jobs/WarriorJob.java index 38dd3a3e..834658c9 100644 --- a/src/main/java/com/erigitic/jobs/jobs/WarriorJob.java +++ b/src/main/java/com/erigitic/jobs/jobs/WarriorJob.java @@ -25,19 +25,33 @@ package com.erigitic.jobs.jobs; -import com.erigitic.jobs.Job; +import com.erigitic.jobs.JobBasedRequirement; import ninja.leaping.configurate.ConfigurationNode; +import java.util.Arrays; + public class WarriorJob implements Job { - public void setupJobValues(ConfigurationNode jobsConfig) { - String[][] killValues = {{"skeleton", "10", "1.00"}, {"zombie", "10", "1.00"}, {"creeper", "10", "1.00"}, {"spider", "10", "1.00"}}; - - for (int i = 0; i < killValues.length; i++) { - jobsConfig.getNode("Warrior", "kill", killValues[i][0], "expreward").setValue(killValues[i][1]); - jobsConfig.getNode("Warrior", "kill", killValues[i][0], "pay").setValue(killValues[i][2]); - } - jobsConfig.getNode("Warrior", "disablesalary").setValue(false); - jobsConfig.getNode("Warrior", "salary").setValue(10); - jobsConfig.getNode("Warrior", "permission").setValue("totaleconomy.job.warrior"); + + private final String NAME = "warrior"; + private final String[] SETS = { "mobs" }; + + @Override + public String getName() { + return NAME; + } + + @Override + public String[] getSets() { + return SETS; + } + + @Override + public void populateNode(ConfigurationNode node) { + node = node.getNode(NAME); + + node.getNode("salary").setValue(10); + node.getNode("sets").setValue(Arrays.asList(SETS)); + + new JobBasedRequirement(null, 0, "totaleconomy.job.warrior").addTo(node); } } diff --git a/src/main/java/com/erigitic/jobs/jobsets/FarmerJobSet.java b/src/main/java/com/erigitic/jobs/jobsets/FarmerJobSet.java new file mode 100644 index 00000000..ca2e839f --- /dev/null +++ b/src/main/java/com/erigitic/jobs/jobsets/FarmerJobSet.java @@ -0,0 +1,64 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs.jobsets; + +import ninja.leaping.configurate.ConfigurationNode; + +public class FarmerJobSet implements JobSet { + + private final String SETNAME = "crops"; + + private final String[][] REWARDS = { + //{"", "", "", "", ""} + // Plants having an age + {"break", "minecraft:wheat", "25", "50.00", "age"}, + {"break", "minecraft:cocoa", "30", "60.00", "age"}, + + {"break", "minecraft:cactus", "10", "15.00", null}, + {"break", "minecraft:reeds", "5", "7.50", null}, + + //These plants aren't hard to grow or harvest thus the low gains + {"break", "minecraft:red_flower", "1", "0.10", null}, + {"break", "minecraft:yellow_flower", "1", "0.10", null}, + {"break", "minecraft:vine", "0", "0.05", null}, + {"break", "minecraft:waterlily", "1", "0.10", null}, + + {"break", "minecraft:red_mushroom", "8", "5.00", null}, + {"break", "minecraft:brown_mushroom", "8", "5.00", null} + }; + + @Override + public void populateNode(ConfigurationNode node) { + ConfigurationNode myNode = node.getNode(SETNAME); + + for (String[] a : REWARDS) { + ConfigurationNode n = myNode.getNode(a[0], a[1]); + n.getNode("exp").setValue(a[2]); + n.getNode("money").setValue(a[3]); + n.getNode("growthTrait").setValue(a[4]); + } + } +} diff --git a/src/main/java/com/erigitic/commands/JobToggleCommand.java b/src/main/java/com/erigitic/jobs/jobsets/FishermanJobSet.java similarity index 57% rename from src/main/java/com/erigitic/commands/JobToggleCommand.java rename to src/main/java/com/erigitic/jobs/jobsets/FishermanJobSet.java index ef891864..03ec4bc9 100644 --- a/src/main/java/com/erigitic/commands/JobToggleCommand.java +++ b/src/main/java/com/erigitic/jobs/jobsets/FishermanJobSet.java @@ -23,32 +23,29 @@ * SOFTWARE. */ -package com.erigitic.commands; +package com.erigitic.jobs.jobsets; -import com.erigitic.config.AccountManager; -import com.erigitic.main.TotalEconomy; -import org.spongepowered.api.command.CommandException; -import org.spongepowered.api.command.CommandResult; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.command.args.CommandContext; -import org.spongepowered.api.command.spec.CommandExecutor; -import org.spongepowered.api.entity.living.player.Player; +import ninja.leaping.configurate.ConfigurationNode; -public class JobToggleCommand implements CommandExecutor { - private AccountManager accountManager; +public class FishermanJobSet implements JobSet { - public JobToggleCommand(TotalEconomy totalEconomy) { - accountManager = totalEconomy.getAccountManager(); - } + private final String SETNAME = "fish"; + + private final String[][] REWARDS = { + //{"", "", "", ""} + {"catch", "cod", "25", "50.00"}, + {"catch", "salmon", "100", "150.00"}, + {"catch", "pufferfish", "250", "300.00"} + }; @Override - public CommandResult execute(CommandSource src, CommandContext args) throws CommandException { - if (src instanceof Player) { - Player sender = ((Player) src).getPlayer().get(); + public void populateNode(ConfigurationNode node) { + ConfigurationNode myNode = node.getNode(SETNAME); - accountManager.toggleNotifications(sender); + for (String[] a : REWARDS) { + ConfigurationNode n = myNode.getNode(a[0], a[1]); + n.getNode("exp").setValue(a[2]); + n.getNode("money").setValue(a[3]); } - - return CommandResult.success(); } } diff --git a/src/main/java/com/erigitic/jobs/jobsets/JobSet.java b/src/main/java/com/erigitic/jobs/jobsets/JobSet.java new file mode 100644 index 00000000..3ae6215b --- /dev/null +++ b/src/main/java/com/erigitic/jobs/jobsets/JobSet.java @@ -0,0 +1,33 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs.jobsets; + +import ninja.leaping.configurate.ConfigurationNode; + +public interface JobSet { + + void populateNode(ConfigurationNode node); +} diff --git a/src/main/java/com/erigitic/jobs/jobsets/LumberjackJobSet.java b/src/main/java/com/erigitic/jobs/jobsets/LumberjackJobSet.java new file mode 100644 index 00000000..c2512054 --- /dev/null +++ b/src/main/java/com/erigitic/jobs/jobsets/LumberjackJobSet.java @@ -0,0 +1,52 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs.jobsets; + +import ninja.leaping.configurate.ConfigurationNode; + +public class LumberjackJobSet implements JobSet { + + private final String SETNAME = "lumberjackSet"; + + private final String[][] REWARDS = { + //{"", "", "", ""} + {"break", "minecraft:log", "10", "1.00"}, + {"break", "minecraft:log2", "10", "1.00"}, + {"break", "minecraft:leaves", "1", "0.01"}, + {"place", "minecraft:sapling", "1", "0.10"} + }; + + @Override + public void populateNode(ConfigurationNode node) { + ConfigurationNode myNode = node.getNode(SETNAME); + + for (String[] a : REWARDS) { + ConfigurationNode n = myNode.getNode(a[0], a[1]); + n.getNode("exp").setValue(a[2]); + n.getNode("money").setValue(a[3]); + } + } +} diff --git a/src/main/java/com/erigitic/jobs/jobsets/MinerJobSet.java b/src/main/java/com/erigitic/jobs/jobsets/MinerJobSet.java new file mode 100644 index 00000000..470234fd --- /dev/null +++ b/src/main/java/com/erigitic/jobs/jobsets/MinerJobSet.java @@ -0,0 +1,56 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs.jobsets; + +import ninja.leaping.configurate.ConfigurationNode; + +public class MinerJobSet implements JobSet { + + private final String SETNAME = "ores"; + + private final String[][] REWARDS = { + //{"", "", "", ""} + {"break", "minecraft:coal_ore", "5", "0.25"}, + {"break", "minecraft:iron_ore", "10", "0.50"}, + {"break", "minecraft:lapis_ore", "20", "4.00"}, + {"break", "minecraft:gold_ore", "40", "5.00"}, + {"break", "minecraft:diamond_ore", "100", "25.00"}, + {"break", "minecraft:emerald_ore", "50", "12.50"}, + {"break", "minecraft:quartz_ore", "5", "0.15"}, + {"break", "minecraft:redstone_ore", "25", "2.00"} + }; + + @Override + public void populateNode(ConfigurationNode node) { + ConfigurationNode myNode = node.getNode(SETNAME); + + for (String[] a : REWARDS) { + ConfigurationNode n = myNode.getNode(a[0], a[1]); + n.getNode("exp").setValue(a[2]); + n.getNode("money").setValue(a[3]); + } + } +} diff --git a/src/main/java/com/erigitic/jobs/jobsets/WarriorJobSet.java b/src/main/java/com/erigitic/jobs/jobsets/WarriorJobSet.java new file mode 100644 index 00000000..c14030c4 --- /dev/null +++ b/src/main/java/com/erigitic/jobs/jobsets/WarriorJobSet.java @@ -0,0 +1,52 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.jobs.jobsets; + +import ninja.leaping.configurate.ConfigurationNode; + +public class WarriorJobSet implements JobSet { + + private final String SETNAME = "mobs"; + + private final String[][] REWARDS = { + //{"", "", "", ""} + {"kill", "skeleton", "10", "1.00"}, + {"kill", "zombie", "10", "1.00"}, + {"kill", "creeper", "10", "1.00"}, + {"kill", "spider", "10", "1.00"} + }; + + @Override + public void populateNode(ConfigurationNode node) { + ConfigurationNode myNode = node.getNode(SETNAME); + + for (String[] a : REWARDS) { + ConfigurationNode n = myNode.getNode(a[0], a[1]); + n.getNode("exp").setValue(a[2]); + n.getNode("money").setValue(a[3]); + } + } +} diff --git a/src/main/java/com/erigitic/main/TotalEconomy.java b/src/main/java/com/erigitic/main/TotalEconomy.java index ee2adc97..8795baae 100644 --- a/src/main/java/com/erigitic/main/TotalEconomy.java +++ b/src/main/java/com/erigitic/main/TotalEconomy.java @@ -28,7 +28,8 @@ import com.erigitic.commands.*; import com.erigitic.config.AccountManager; import com.erigitic.config.TECurrency; -import com.erigitic.jobs.TEJobs; +import com.erigitic.jobs.TEJobManager; +import com.erigitic.sql.SQLHandler; import com.google.inject.Inject; import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.commented.CommentedConfigurationNode; @@ -55,11 +56,8 @@ import java.io.File; import java.io.IOException; import java.math.BigDecimal; -import java.text.NumberFormat; -import java.text.ParsePosition; -import java.util.Optional; -@Plugin(id = "totaleconomy", name = "Total Economy", version = "1.5.3", description = "All in one economy plugin for Minecraft/Sponge") +@Plugin(id = "totaleconomy", name = "Total Economy", version = "1.6.0-DEV", description = "All in one economy plugin for Minecraft/Sponge") public class TotalEconomy { @Inject @@ -91,42 +89,68 @@ public class TotalEconomy { private AccountManager accountManager; - private TEJobs teJobs; + private TEJobManager teJobManager; private boolean loadJobs = true; - private boolean loadSalary = true; private boolean jobPermissions = false; private boolean jobNotifications = true; - private boolean loadMoneyCap = false; + private boolean loadSalary = true; + + private boolean databaseActive = false; + private String databaseUrl; + private String databaseUser; + private String databasePassword; + + private boolean loadMoneyCap = false; private BigDecimal moneyCap; + private int saveInterval; + + private SQLHandler sqlHandler; + @Listener public void preInit(GamePreInitializationEvent event) { setupConfig(); - defaultCurrency = new TECurrency(Text.of(config.getNode("currency-singular").getValue()), - Text.of(config.getNode("currency-plural").getValue()), Text.of(config.getNode("symbol").getValue()), 2, true); + defaultCurrency = new TECurrency( + Text.of(config.getNode("currency", "currency-singular").getValue()), + Text.of(config.getNode("currency", "currency-plural").getValue()), + Text.of(config.getNode("currency", "symbol").getValue()), + 2, + true, + config.getNode("currency", "prefix-symbol").getBoolean() + ); loadJobs = config.getNode("features", "jobs", "enable").getBoolean(); loadSalary = config.getNode("features", "jobs", "salary").getBoolean(); + databaseActive = config.getNode("database", "enable").getBoolean(); jobPermissions = config.getNode("features", "jobs", "permissions").getBoolean(); jobNotifications = config.getNode("features", "jobs", "notifications").getBoolean(); loadMoneyCap = config.getNode("features", "moneycap", "enable").getBoolean(); + if (databaseActive) { + databaseUrl = config.getNode("database", "url").getString(); + databaseUser = config.getNode("database", "user").getString(); + databasePassword = config.getNode("database", "password").getString(); + + sqlHandler = new SQLHandler(this); + } + + saveInterval = config.getNode("save-interval").getInt(30); + accountManager = new AccountManager(this); game.getServiceManager().setProvider(this, EconomyService.class, accountManager); //Only setup job stuff if config is set to load jobs - if (loadJobs == true) { - teJobs = new TEJobs(this); + if (loadJobs) { + teJobManager = new TEJobManager(this); } if (loadMoneyCap == true) { - moneyCap = BigDecimal.valueOf(config.getNode("features", "moneycap", "amount").getFloat()) - .setScale(2, BigDecimal.ROUND_DOWN); + moneyCap = BigDecimal.valueOf(config.getNode("features", "moneycap", "amount").getFloat()).setScale(2, BigDecimal.ROUND_DOWN); } } @@ -134,8 +158,9 @@ public void preInit(GamePreInitializationEvent event) { public void init(GameInitializationEvent event) { createAndRegisterCommands(); - if (loadJobs) - game.getEventManager().registerListeners(this, teJobs); + if (loadJobs) { + game.getEventManager().registerListeners(this, teJobManager); + } } @Listener @@ -153,7 +178,10 @@ public void onServerStart(GameStartedServerEvent event) { @Listener public void onServerStopping(GameStoppingServerEvent event) { logger.info("Total Economy Stopping"); - accountManager.saveAccountConfig(); + + if (!databaseActive) { + accountManager.saveAccountConfig(); + } } @Listener @@ -163,11 +191,9 @@ public void onServerStop(GameStoppedServerEvent event) { @Listener public void onPlayerJoin(ClientConnectionEvent.Join event) { - if (event.getTargetEntity() instanceof Player) { - Player player = event.getTargetEntity(); + Player player = event.getTargetEntity(); - accountManager.getOrCreateAccount(player.getUniqueId()); - } + accountManager.getOrCreateAccount(player.getUniqueId()); } /** @@ -177,9 +203,9 @@ public void onPlayerJoin(ClientConnectionEvent.Join event) { */ @Listener public void onGameReload(GameReloadEvent event) { - // If jobs are set to load, then reload the jobs config - if (loadJobs) - teJobs.reloadConfig(); + if (loadJobs) { + teJobManager.reloadJobsAndSets(); + } accountManager.reloadConfig(); } @@ -192,20 +218,26 @@ private void setupConfig() { config = loader.load(); if (!defaultConf.exists()) { + config.getNode("database", "enable").setValue(databaseActive); + config.getNode("database", "url").setValue("jdbc:mysql://[URL]:[PORT]/"); + config.getNode("database", "user").setValue(""); + config.getNode("database", "password").setValue(""); config.getNode("features", "jobs", "enable").setValue(loadJobs); config.getNode("features", "jobs", "salary").setValue(loadSalary); config.getNode("features", "jobs", "permissions").setValue(jobPermissions); config.getNode("features", "jobs", "notifications").setValue(true); config.getNode("features", "moneycap", "enable").setValue(loadMoneyCap); config.getNode("features", "moneycap", "amount").setValue(10000000); - config.getNode("startbalance").setValue(100); - config.getNode("currency-singular").setValue("Dollar"); - config.getNode("currency-plural").setValue("Dollars"); - config.getNode("symbol").setValue("$"); + config.getNode("currency", "startbalance").setValue(100); + config.getNode("currency", "currency-singular").setValue("Dollar"); + config.getNode("currency", "currency-plural").setValue("Dollars"); + config.getNode("currency", "symbol").setValue("$"); + config.getNode("currency", "prefix-symbol").setValue(true); + config.getNode("save-interval").setValue(30); loader.save(config); } } catch (IOException e) { - logger.warn("Main config could not be loaded/created/changed!"); + logger.warn("[TE] Main configuration file could not be loaded/created/changed!"); } } @@ -222,7 +254,7 @@ private void createAndRegisterCommands() { .description(Text.of("Pay a player without removing money from your balance.")) .permission("totaleconomy.command.adminpay") .executor(new AdminPayCommand(this)) - .arguments(GenericArguments.player(Text.of("player")), + .arguments(GenericArguments.user(Text.of("player")), GenericArguments.string(Text.of("amount"))) .build(); @@ -249,72 +281,29 @@ private void createAndRegisterCommands() { .description(Text.of("Set a player's balance")) .permission("totaleconomy.command.setbalance") .executor(new SetBalanceCommand(this)) - .arguments(GenericArguments.player(Text.of("player")), + .arguments(GenericArguments.user(Text.of("player")), GenericArguments.string(Text.of("amount"))) .build(); //Only enables job commands if the value for jobs in config is set to true - if (loadJobs == true) { - CommandSpec jobSetCmd = CommandSpec.builder() - .description(Text.of("Set your job")) - .permission("totaleconomy.command.jobset") - .executor(new JobCommand(this)) - .arguments(GenericArguments.string(Text.of("jobName"))) - .build(); - - CommandSpec jobNotifyToggle = CommandSpec.builder() - .description(Text.of("Toggle job notifications on/off")) - .permission("totaleconomy.command.jobtoggle") - .executor(new JobToggleCommand(this)) - .build(); - - CommandSpec jobInfoCmd = CommandSpec.builder() - .description(Text.of("Prints out a list of items that reward exp and money for the current job")) - .permission("totaleconomy.command.jobinfo") - .executor(new JobInfoCommand(this)) - .build(); - - CommandSpec jobCommand = CommandSpec.builder() - .description(Text.of("Display list of jobs.")) - .permission("totaleconomy.command.job") - .executor(new JobCommand(this)) - .child(jobSetCmd, "set", "s") - .child(jobNotifyToggle, "toggle", "t") - .child(jobInfoCmd, "info", "i") - .build(); - - - game.getCommandManager().register(this, jobCommand, "job"); + if (loadJobs) { + game.getCommandManager().register(this, JobCommand.commandSpec(this), "job"); } game.getCommandManager().register(this, payCommand, "pay"); game.getCommandManager().register(this, adminPayCommand, "adminpay"); - game.getCommandManager().register(this, balanceCommand, "balance", "bal"); + game.getCommandManager().register(this, balanceCommand, "balance", "bal", "money"); game.getCommandManager().register(this, viewBalanceCommand, "viewbalance", "vbal"); game.getCommandManager().register(this, setBalanceCommand, "setbalance", "setbal"); game.getCommandManager().register(this, balanceTopCommand, "balancetop", "baltop"); } - /** - * Determines if the String passed in is numeric or not - * - * @param str the String to check - * - * @return boolean whether or not the String is numeric - */ - public static boolean isNumeric(String str) { - NumberFormat formatter = NumberFormat.getInstance(); - ParsePosition pos = new ParsePosition(0); - formatter.parse(str, pos); - return str.length() == pos.getIndex(); - } - public AccountManager getAccountManager() { return accountManager; } - public TEJobs getTEJobs() { - return teJobs; + public TEJobManager getTEJobManager() { + return teJobManager; } public Logger getLogger() { @@ -325,10 +314,10 @@ public File getConfigDir() { return configDir; } - public BigDecimal getStartingBalance() { return new BigDecimal(config.getNode("startbalance").getString()); } + public BigDecimal getStartingBalance() { return new BigDecimal(config.getNode("currency", "startbalance").getString()); } public String getCurrencySymbol() { - return config.getNode("symbol").getValue().toString(); + return config.getNode("currency", "symbol").getString(); } public Server getServer() { @@ -353,14 +342,30 @@ public boolean isLoadMoneyCap() { return loadMoneyCap; } + public boolean isDatabaseActive() { return databaseActive; } + + public boolean isSymbolPrefixed() { return config.getNode("currency", "prefix-symbol").getBoolean(); } + public BigDecimal getMoneyCap() { return moneyCap.setScale(2, BigDecimal.ROUND_DOWN); } + public int getSaveInterval() { + return saveInterval; + } + public boolean hasJobNotifications() { return jobNotifications; } public UserStorageService getUserStorageService() { return userStorageService; } + public String getDatabaseUrl() { return databaseUrl; } + + public String getDatabaseUser() { return databaseUser; } + + public String getDatabasePassword() { return databasePassword; } + + public SQLHandler getSqlHandler() { return sqlHandler; } + } \ No newline at end of file diff --git a/src/main/java/com/erigitic/sql/SQLHandler.java b/src/main/java/com/erigitic/sql/SQLHandler.java new file mode 100644 index 00000000..4b046fe1 --- /dev/null +++ b/src/main/java/com/erigitic/sql/SQLHandler.java @@ -0,0 +1,106 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.sql; + +import com.erigitic.main.TotalEconomy; +import com.google.common.util.concurrent.UncheckedExecutionException; +import org.slf4j.Logger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.service.sql.SqlService; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class SQLHandler { + private TotalEconomy totalEconomy; + private Logger logger; + public DataSource dataSource; + private SqlService sql; + private final String dbName = "totaleconomy"; + + public SQLHandler(TotalEconomy totalEconomy) { + this.totalEconomy = totalEconomy; + logger = totalEconomy.getLogger(); + + try { + dataSource = getDataSource(totalEconomy.getDatabaseUrl() + "?user=" + totalEconomy.getDatabaseUser() + "&password=" + totalEconomy.getDatabasePassword()); + } catch (SQLException e) { + logger.warn("Error getting data source!"); + } catch (UncheckedExecutionException e) { + logger.warn("Error connecting to database! Check the config and make sure the database credentials are correct!"); + } + } + + /** + * Get the data source using the passed in JBDC url + * + * @param jdbcUrl + * @return DataSource + * @throws SQLException + */ + public DataSource getDataSource(String jdbcUrl) throws SQLException { + if (sql == null) { + sql = Sponge.getServiceManager().provide(SqlService.class).get(); + } + + return sql.getDataSource(jdbcUrl); + } + + public boolean createDatabase() { + try { + Connection conn = dataSource.getConnection(); + + boolean result = conn.prepareStatement("CREATE DATABASE IF NOT EXISTS totaleconomy").execute(); + + conn.close(); + + return result; + } catch (SQLException e) { + logger.warn("[TE] An error occurred while creating the totaleconomy database!"); + e.printStackTrace(); + } + + return false; + } + + public boolean createTable(String tableName, String cols) { + try { + Connection conn = dataSource.getConnection(); + + boolean result = conn.prepareStatement("CREATE TABLE IF NOT EXISTS " + dbName + "." + tableName + " (" + cols + ")").execute(); + + conn.close(); + + return result; + } catch (SQLException e) { + logger.warn("[TE] An error occurred while creating a table!"); + e.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/com/erigitic/sql/SQLQuery.java b/src/main/java/com/erigitic/sql/SQLQuery.java new file mode 100644 index 00000000..87dd4b14 --- /dev/null +++ b/src/main/java/com/erigitic/sql/SQLQuery.java @@ -0,0 +1,327 @@ +/* + * This file is part of Total Economy, licensed under the MIT License (MIT). + * + * Copyright (c) Eric Grandt + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.erigitic.sql; + +import javax.sql.DataSource; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; + +public class SQLQuery { + private String statement; + private String errorMessage; + private DataSource dataSource; + private ResultSet resultSet; + private int rowsAffected = 0; + + private SQLQuery(Builder builder) { + statement = builder.statement; + errorMessage = builder.errorMessage; + dataSource = builder.dataSource; + + if (builder.update) + executeUpdate(); + else + executeQuery(); + } + + public static SQLQuery.Builder builder(DataSource dataSource) { + return new Builder(dataSource); + } + + public void executeQuery() { + try { + Connection conn = dataSource.getConnection(); + + Optional resultSetOpt = Optional.of(conn.prepareStatement(statement).executeQuery()); + + if (resultSetOpt.isPresent()) + resultSet = resultSetOpt.get(); + + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * Executes statements that return an integer (update, insert, delete). + * + * @return int number of rows affected by the query + */ + public int executeUpdate() { + try { + Connection conn = dataSource.getConnection(); + + rowsAffected = conn.prepareStatement(statement).executeUpdate(); + + conn.close(); + + return rowsAffected; + } catch (SQLException e) { + e.printStackTrace(); + } + + return 0; + } + + public boolean recordExists() { + try { + if (resultSet.isBeforeFirst()) + return true; + } catch (SQLException e) { + e.printStackTrace(); + } + + return false; + } + + /** + * Gets a boolean from the executed SQLQuery. Throws a NPE when no boolean is returned. + * + * @return boolean value of column + */ + public boolean getBoolean() { + try { + if (resultSet.next()) + return resultSet.getBoolean(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + throw new NullPointerException("[SQL] Could not retrieve boolean from database!"); + } + + /** + * Gets a boolean from the executed SQLQuery. Returns a default value if no boolean is present. + * + * @param def default value to be returned + * @return boolean value of column + */ + public boolean getBoolean(boolean def) { + try { + if (resultSet.next()) + return resultSet.getBoolean(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + return def; + } + + /** + * Gets an int from the executed SQLQuery. Throws a NPE when no int is returned. + * + * @return int value of column + */ + public int getInt() { + try { + if (resultSet.next()) + return resultSet.getInt(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + throw new NullPointerException("[SQL] Could not retrieve integer from database!"); + } + + /** + * Gets an int from the executed SQLQuery. Returns a default value if no int is present. + * + * @param def default value to be returned + * @return int value of column + */ + public int getInt(int def) { + try { + if (resultSet.next()) + return resultSet.getInt(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + return def; + } + + /** + * Gets a BigDecimal from the executed SQLQuery. Throws a NPE when no BigDecimal is returned. + * + * @return BigDecimal value of column + */ + public BigDecimal getBigDecimal() { + try { + if (resultSet.next()) + return resultSet.getBigDecimal(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + throw new NullPointerException("[SQL] Could not retrieve BigDecimal from database!"); + } + + /** + * Gets a BigDecimal from the executed SQLQuery. Returns a default value if no BigDecimal is present. + * + * @param def default value to be returned + * @return BigDecimal value of column + */ + public BigDecimal getBigDecimal(BigDecimal def) { + try { + if (resultSet.next()) + return resultSet.getBigDecimal(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + return def; + } + + /** + * Gets a string from the executed SQLQuery. Throws a NPE when no string is returned. + * + * @return string value of column + */ + public String getString() { + try { + if (resultSet.next()) + return resultSet.getString(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + throw new NullPointerException("[SQL] Could not retrieve string from database!"); + } + + /** + * Gets a string from the executed SQLQuery. Returns a default value if no string is present. + * + * @param def default value to be returned + * @return string value of column + */ + public String getString(String def) { + try { + if (resultSet.next()) + return resultSet.getString(1); + } catch (SQLException e) { + e.printStackTrace(); + } + + return def; + } + + public int getRowsAffected() { + return rowsAffected; + } + + public static class Builder { + private DataSource dataSource; + private String statement = ""; + private String errorMessage = ""; + + private boolean update = false; + + public Builder(DataSource dataSource) { + this.dataSource = dataSource; + } + + public Builder select(String column) { + statement += "SELECT " + column; + + return this; + } + + public Builder from(String table) { + statement += " FROM " + table; + + return this; + } + + public Builder where(String comp) { + statement += " WHERE " + comp; + + return this; + } + + // TODO: Not really needed anymore + public Builder equals(String val) { + statement += "='" + val + "'"; + + return this; + } + + public Builder and(String comp) { + statement += " AND " + comp; + + return this; + } + + public Builder insert(String table) { + statement += "INSERT IGNORE INTO " + table; + + return this; + } + + public Builder columns(String... columns) { + // Join all the values with a comma deliminator and surround with () + String columnsJoined = " (" + String.join(",", columns) + ")"; + statement += columnsJoined; + + return this; + } + + public Builder values(String... values) { + String valuesJoined = "('" + String.join("','", values) + "')"; + + statement += " VALUES " + valuesJoined; + + return this; + } + + public Builder update(String table) { + update = true; + statement += "UPDATE " + table; + + return this; + } + + public Builder set(String column) { + statement += " SET " + column; + + return this; + } + + public Builder onError(String errorMessage) { + this.errorMessage = errorMessage; + + return this; + } + + public SQLQuery build() { + return new SQLQuery(this); + } + } +}