EpicKnarvik97 65d1471a8f
All checks were successful
KnarCraft/PlayerPayouts/pipeline/head This commit looks good
Fix #3
2024-01-25 16:17:26 +01:00

422 lines
17 KiB
Java

package net.knarcraft.playerpayouts.config;
import net.knarcraft.playerpayouts.PlayerPayouts;
import net.knarcraft.playerpayouts.config.payout.PayoutAction;
import net.knarcraft.playerpayouts.config.payout.PayoutActionParser;
import net.knarcraft.playerpayouts.config.payout.PayoutComponent;
import net.knarcraft.playerpayouts.config.payout.PayoutDelimiter;
import net.knarcraft.playerpayouts.config.payout.PayoutTarget;
import net.knarcraft.playerpayouts.manager.PermissionManager;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
/**
* This plugin's configuration
*/
public class Configuration {
private FileConfiguration fileConfiguration;
private Map<String, Double> groupPayouts;
private Map<UUID, Double> playerPayouts;
private double defaultPayout;
private int hoursUntilBonus;
private double bonusMultiplier;
private int payoutDelay;
private double afkPercentage;
private boolean displayPaymentMessage;
private PayoutComponent payoutComponent;
/**
* Instantiates a new configuration
*
* @param fileConfiguration <p>The file configuration to read values from</p>
*/
public Configuration(@NotNull FileConfiguration fileConfiguration) {
load(fileConfiguration);
}
/**
* Loads the configuration specified in the given file configuration
*
* @param fileConfiguration <p>The file configuration to load</p>
*/
public void load(@NotNull FileConfiguration fileConfiguration) {
groupPayouts = new HashMap<>();
playerPayouts = new HashMap<>();
this.fileConfiguration = fileConfiguration;
// Load per-group payouts
ConfigurationSection groupPayoutsSection = fileConfiguration.getConfigurationSection(
ConfigurationKey.GROUP_PAYOUTS.getPath());
if (groupPayoutsSection != null) {
for (String key : groupPayoutsSection.getKeys(false)) {
groupPayouts.put(key, groupPayoutsSection.getDouble(key));
}
}
// Load per-player payouts
ConfigurationSection playerPayoutsSection = fileConfiguration.getConfigurationSection(
ConfigurationKey.PLAYER_PAYOUTS.getPath());
if (playerPayoutsSection != null) {
for (String key : playerPayoutsSection.getKeys(false)) {
playerPayouts.put(UUID.fromString(key), playerPayoutsSection.getDouble(key));
}
}
// Load simple configuration values
this.defaultPayout = fileConfiguration.getDouble(ConfigurationKey.DEFAULT_PAYOUT.getPath(), 10);
this.hoursUntilBonus = fileConfiguration.getInt(ConfigurationKey.HOURS_UNTIL_BONUS.getPath(), 100);
this.bonusMultiplier = fileConfiguration.getDouble(ConfigurationKey.BONUS_MULTIPLIER.getPath(), 1);
this.payoutDelay = fileConfiguration.getInt(ConfigurationKey.PAYOUT_DELAY.getPath(), 60);
this.afkPercentage = fileConfiguration.getDouble(ConfigurationKey.AFK_PERCENTAGE.getPath(), 0);
this.displayPaymentMessage = fileConfiguration.getBoolean(ConfigurationKey.DISPLAY_PAYMENT_MESSAGE.getPath(),
true);
// Parse the payout rules
try {
this.payoutComponent = PayoutActionParser.matchPayoutComponent(fileConfiguration.getString(
ConfigurationKey.PAYOUT_RULES.getPath(), "p,hg,b"));
} catch (ParseException exception) {
PlayerPayouts.getInstance().getLogger().log(Level.SEVERE, "Unable to parse your payout rules! Please " +
"check your configuration file! Falling back to default!");
PlayerPayouts.getInstance().getLogger().log(Level.SEVERE, exception.getMessage());
try {
this.payoutComponent = PayoutActionParser.matchPayoutComponent("p,hg,b");
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
/**
* Gets the number of hours a player must have played to receive a payment bonus
*
* @return <p>The min. amount of hours required to get a bonus</p>
*/
public int getHoursUntilBonus() {
return hoursUntilBonus;
}
/**
* A multiplier to apply to the bonus
*
* <p>By default, the bonus is total playtime / hours until bonus. This multiplier allows adjusting how high or low
* the bonus becomes. For example, a bonus multiplier of 0.5 would halve the paid bonus, while a bonus multiplier
* of 2 would double the paid bonus.</p>
*
* @return <p>The bonus multiplier</p>
*/
public double getBonusMultiplier() {
return bonusMultiplier;
}
/**
* Gets the base pay for the given player
*
* @param player <p>The player to get the base pay for</p>
* @return <p>The base pay for the player</p>
*/
public double getBasePay(@NotNull Player player) {
double pay = calculatePayoutFromComponent(payoutComponent, player);
if (pay != 0) {
return pay;
} else {
return getDefaultBasePay(player);
}
}
/**
* Gets the base payout to pay the given player
*
* @param player <p>The player to get the base payout for</p>
* @return <p>The player's base pay</p>
*/
public double getDefaultBasePay(@NotNull Player player) {
if (playerPayouts.get(player.getUniqueId()) != null) {
return playerPayouts.get(player.getUniqueId());
}
double groupPayout = 0;
if (PermissionManager.isInitialized()) {
groupPayout = getGroupPayout(player, true);
}
if (groupPayout == 0) {
return defaultPayout;
} else {
return groupPayout;
}
}
/**
* Gets the max payout of the given player's permission groups
*
* @param player <p>The player to get the group payout for</p>
* @param highest <p>Whether to just get the highest value, instead of the sum</p>
* @return <p>The group payout, or -1 if no groups has a set payout</p>
*/
private double getGroupPayout(@NotNull Player player, boolean highest) {
if (highest) {
return getHighestGroupPayout(player);
} else {
return getSumGroupPayout(player);
}
}
/**
* Gets the max payout of the given player's permission groups
*
* @param player <p>The player to get the group payout for</p>
* @return <p>The group payout, or -1 if no groups has a set payout</p>
*/
private double getSumGroupPayout(@NotNull Player player) {
double total = 0;
for (String group : PermissionManager.getPlayerGroups(player)) {
if (groupPayouts.containsKey(group)) {
Double groupPayout = groupPayouts.get(group);
if (groupPayout != null) {
total += groupPayout;
}
}
}
return total;
}
/**
* Gets the max payout of the given player's permission groups
*
* @param player <p>The player to get the group payout for</p>
* @return <p>The group payout, or -1 if no groups has a set payout</p>
*/
private double getHighestGroupPayout(@NotNull Player player) {
double maxPay = 0;
for (String group : PermissionManager.getPlayerGroups(player)) {
if (groupPayouts.containsKey(group)) {
Double groupPayout = groupPayouts.get(group);
if (groupPayout != null) {
maxPay = Math.max(maxPay, groupPayout);
}
}
}
return maxPay;
}
/**
* The delay between each time a player is paid in minutes
*
* @return <p>The delay between payments</p>
*/
public int getPayoutDelay() {
return payoutDelay;
}
/**
* The percentage of the payment to pay AFK players
*
* @return <p>AFK players' payment percentage</p>
*/
public double getAfkPercentage() {
return afkPercentage;
}
/**
* Whether to display a message to players when they're paid
*
* @return <p>True if a payment message should be displayed</p>
*/
public boolean displayPaymentMessage() {
return displayPaymentMessage;
}
/**
* Sets the payout given to a specific player
*
* @param playerId <p>The id of the player to set payout for</p>
* @param payout <p>The payout to set for the player</p>
*/
public void setPlayerPayout(@NotNull UUID playerId, @Nullable Double payout) {
if (payout == null || payout < 0) {
this.playerPayouts.put(playerId, null);
} else {
this.playerPayouts.put(playerId, payout);
}
this.save();
}
/**
* Sets the payout given to a specific group
*
* @param groupName <p>The name of the group to set payout for</p>
* @param payout <p>The payout to set for the group</p>
*/
public void setGroupPayout(@NotNull String groupName, @Nullable Double payout) {
if (payout == null || payout < 0) {
this.groupPayouts.put(groupName, null);
} else {
this.groupPayouts.put(groupName, payout);
}
this.save();
}
/**
* Gets all group payout overrides
*
* @return <p>All group payout overrides</p>
*/
public @NotNull Map<String, Double> getGroupPayouts() {
return groupPayouts;
}
/**
* Gets a group payout override
*
* @param groupName <p>The group to get the override of</p>
* @return <p>The overridden payout, or null if missing or cleared</p>
*/
public @Nullable Double getGroupPayout(@NotNull String groupName) {
return groupPayouts.get(groupName);
}
/**
* Gets all player payout overrides
*
* @return <p>All player payout overrides</p>
*/
public @NotNull Map<UUID, Double> getPlayerPayouts() {
return playerPayouts;
}
/**
* Gets a player payout override
*
* @param playerId <p>The id of the player to get the override of</p>
* @return <p>The overridden payout, or null if missing or cleared</p>
*/
public @Nullable Double getPlayerPayout(@NotNull UUID playerId) {
return playerPayouts.get(playerId);
}
/**
* Saves this configuration to disk
*/
public void save() {
fileConfiguration.set(ConfigurationKey.PAYOUT_DELAY.getPath(), this.payoutDelay);
fileConfiguration.setComments(ConfigurationKey.PAYOUT_DELAY.getPath(),
List.of("The amount of minutes to wait between each payment"));
fileConfiguration.set(ConfigurationKey.BONUS_MULTIPLIER.getPath(), this.bonusMultiplier);
fileConfiguration.setComments(ConfigurationKey.BONUS_MULTIPLIER.getPath(),
List.of("A multiplier used to increase or decrease the time bonus ((hours played / hours until " +
"bonus) * bonusMultiplier) + payout"));
fileConfiguration.set(ConfigurationKey.DEFAULT_PAYOUT.getPath(), this.defaultPayout);
fileConfiguration.setComments(ConfigurationKey.DEFAULT_PAYOUT.getPath(),
List.of("The default payout if the player has no overrides"));
fileConfiguration.set(ConfigurationKey.DISPLAY_PAYMENT_MESSAGE.getPath(), this.displayPaymentMessage);
fileConfiguration.setComments(ConfigurationKey.DISPLAY_PAYMENT_MESSAGE.getPath(),
List.of("Whether to announce to a player that they've just been paid"));
fileConfiguration.set(ConfigurationKey.AFK_PERCENTAGE.getPath(), this.afkPercentage);
fileConfiguration.setComments(ConfigurationKey.AFK_PERCENTAGE.getPath(),
List.of("The percentage of their normal payout to pay AFK players"));
fileConfiguration.set(ConfigurationKey.PAYOUT_DELAY.getPath(), this.payoutDelay);
fileConfiguration.setComments(ConfigurationKey.PAYOUT_DELAY.getPath(),
List.of("The amount of minutes to wait between each payment"));
fileConfiguration.set(ConfigurationKey.HOURS_UNTIL_BONUS.getPath(), this.hoursUntilBonus);
fileConfiguration.setComments(ConfigurationKey.HOURS_UNTIL_BONUS.getPath(),
List.of("The amount of hours until a bonus is given. Set to -1 to disable."));
for (Map.Entry<UUID, Double> playerPayout : this.playerPayouts.entrySet()) {
fileConfiguration.set(ConfigurationKey.PLAYER_PAYOUTS.getPath() + "." + playerPayout.getKey().toString(),
playerPayout.getValue());
}
fileConfiguration.setComments(ConfigurationKey.PLAYER_PAYOUTS.getPath(),
List.of("Overrides for specific players"));
for (Map.Entry<String, Double> groupPayout : this.groupPayouts.entrySet()) {
fileConfiguration.set(ConfigurationKey.GROUP_PAYOUTS.getPath() + "." + groupPayout.getKey(),
groupPayout.getValue());
}
fileConfiguration.setComments(ConfigurationKey.GROUP_PAYOUTS.getPath(),
List.of("Overrides for specific groups"));
fileConfiguration.setComments(ConfigurationKey.PAYOUT_RULES.getPath(), List.of("The rules for how the base payout " +
"is calculated. \",\" = OR, \"+\" = AND. \"p\" or \"player\" is the override for a specific player. \"g\"",
" or \"groups\" is the sum of all group overrides for a specific player. \"hg\" or \"HighestGroup\" " +
"is the highest sum of all", " of a specific player's groups. \"b\" or \"base\" is the default" +
" base payment.", "", "If you wanted to give players the sum of everything, you'd set it to " +
"\"p+g+b\".", "If you wanted to give players the sum of their personal override and their " +
"highest group, but fall back to the base", " pay, you'd set it to: \"p+hg,b\"", "If the payout" +
" rule you set ends up giving 0 as the payment, it will fall back to the default of \"p,hg,b\""));
PlayerPayouts.getInstance().saveConfig();
// Remove any null values, as those aren't useful except for clearing values
for (Map.Entry<String, Double> entry : groupPayouts.entrySet()) {
if (entry.getValue() == null) {
groupPayouts.remove(entry.getKey());
}
}
for (Map.Entry<UUID, Double> entry : playerPayouts.entrySet()) {
if (entry.getValue() == null) {
playerPayouts.remove(entry.getKey());
}
}
}
/**
* Calculates payment from the given payout component
*
* @param payoutComponent <p>The payout component to calculate from</p>
* @param player <p>The player to calculate payment for</p>
* @return <p></p>
*/
private double calculatePayoutFromComponent(@NotNull PayoutComponent payoutComponent, @NotNull Player player) {
if (payoutComponent instanceof PayoutTarget target) {
// Get the value of the payout target
return switch (target) {
case BASE -> defaultPayout;
case PLAYER -> playerPayouts.getOrDefault(player.getUniqueId(), 0d);
case GROUPS -> getGroupPayout(player, false);
case HIGHEST_GROUP -> getGroupPayout(player, true);
};
} else if (payoutComponent instanceof PayoutAction action) {
PayoutDelimiter delimiter = action.delimiter();
if (delimiter == PayoutDelimiter.ADD) {
// Return the sum of the two components
return calculatePayoutFromComponent(action.component1(), player) +
calculatePayoutFromComponent(action.component2(), player);
} else if (delimiter == PayoutDelimiter.OR) {
// Get the result of the first component, or the second one if the first is zero
double value = calculatePayoutFromComponent(action.component1(), player);
if (value > 0) {
return value;
} else {
return calculatePayoutFromComponent(action.component2(), player);
}
} else {
throw new NotImplementedException("The encountered payout delimiter is not implemented. " +
"Please inform the developer!");
}
} else {
throw new NotImplementedException("An unknown type of payout component was encountered." +
"Please inform the developer!");
}
}
}