Add configurable score buckets
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
This file is part of ScoreKeeper.
|
||||
|
||||
ScoreKeeper is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ScoreKeeper is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with ScoreKeeper. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
*/
|
||||
|
||||
package com.majinnaibu.minecraft.plugins.scorekeeper;
|
||||
|
||||
public final class ScoreBucket {
|
||||
private final String _id;
|
||||
private final String _singular;
|
||||
private final String _plural;
|
||||
private final int _initialValue;
|
||||
|
||||
public ScoreBucket(String id, String singular, String plural, int initialValue) {
|
||||
if (id == null || !id.matches("[A-Za-z0-9_-]+")) {
|
||||
throw new IllegalArgumentException("Bucket ID must contain only letters, numbers, _ or -");
|
||||
}
|
||||
if (singular == null || singular.isBlank()) {
|
||||
throw new IllegalArgumentException("Bucket singular label cannot be blank");
|
||||
}
|
||||
if (plural == null || plural.isBlank()) {
|
||||
throw new IllegalArgumentException("Bucket plural label cannot be blank");
|
||||
}
|
||||
_id = id;
|
||||
_singular = singular;
|
||||
_plural = plural;
|
||||
_initialValue = initialValue;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
public String getSingular() {
|
||||
return _singular;
|
||||
}
|
||||
|
||||
public String getPlural() {
|
||||
return _plural;
|
||||
}
|
||||
|
||||
public int getInitialValue() {
|
||||
return _initialValue;
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,14 @@ package com.majinnaibu.minecraft.plugins.scorekeeper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.commands.ScoreAddCommand;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.commands.ScoreArchiveCommand;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.commands.ScoreBucketCommand;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.commands.ScoreGetCommand;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.commands.ScoreResetCommand;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.commands.ScoreSubtractCommand;
|
||||
@@ -33,12 +35,16 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
private final HashMap<UUID, Integer> _playerScores = new HashMap<UUID, Integer>();
|
||||
private final Map<String, ScoreBucket> _buckets = new LinkedHashMap<>();
|
||||
private final Map<String, Map<UUID, Integer>> _bucketScores = new LinkedHashMap<>();
|
||||
private final Map<UUID, String> _playerBuckets = new LinkedHashMap<>();
|
||||
private String _defaultBucketId = "points";
|
||||
public final String _logPrefix = "[ScoreKeeper] ";
|
||||
public final Component _messagePrefix =
|
||||
Component.text("[")
|
||||
@@ -48,8 +54,19 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
@Override
|
||||
public void onDisable() {
|
||||
YamlConfiguration scores = new YamlConfiguration();
|
||||
for (var entry : _playerScores.entrySet()) {
|
||||
scores.set(entry.getKey().toString(), entry.getValue());
|
||||
scores.set("version", 2);
|
||||
scores.set("default-bucket", _defaultBucketId);
|
||||
for (ScoreBucket bucket : _buckets.values()) {
|
||||
String path = "buckets." + bucket.getId();
|
||||
scores.set(path + ".singular", bucket.getSingular());
|
||||
scores.set(path + ".plural", bucket.getPlural());
|
||||
scores.set(path + ".initial-value", bucket.getInitialValue());
|
||||
for (var entry : _bucketScores.get(bucket.getId()).entrySet()) {
|
||||
scores.set(path + ".scores." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
for (var entry : _playerBuckets.entrySet()) {
|
||||
scores.set("player-buckets." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
try {
|
||||
scores.save(new File(getDataFolder(), "scores.yml"));
|
||||
@@ -65,18 +82,11 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
getCommand("score-subtract").setExecutor(new ScoreSubtractCommand(this));
|
||||
getCommand("score-reset").setExecutor(new ScoreResetCommand(this));
|
||||
getCommand("score-archive").setExecutor(new ScoreArchiveCommand(this));
|
||||
getCommand("score-bucket").setExecutor(new ScoreBucketCommand(this));
|
||||
|
||||
File scoreFile = new File(getDataFolder(), "scores.yml");
|
||||
if (scoreFile.isFile()) {
|
||||
YamlConfiguration scores = YamlConfiguration.loadConfiguration(scoreFile);
|
||||
for (String key : scores.getKeys(false)) {
|
||||
try {
|
||||
_playerScores.put(UUID.fromString(key), scores.getInt(key));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logWarning("Ignoring score with invalid player UUID: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
_buckets.put("points", new ScoreBucket("points", "point", "points", 0));
|
||||
_bucketScores.put("points", new LinkedHashMap<>());
|
||||
loadScores();
|
||||
|
||||
logInfo(
|
||||
getPluginMeta().getName() + " version " + getPluginMeta().getVersion() + " is enabled.");
|
||||
@@ -84,8 +94,11 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
|
||||
// region Commands
|
||||
public void addScore(Player player, int amount) {
|
||||
int oldScore = getPlayerScore(player);
|
||||
setPlayerScore(player, oldScore + amount);
|
||||
addScore(player, getPlayerBucket(player), amount);
|
||||
}
|
||||
|
||||
public void addScore(Player player, String bucketId, int amount) {
|
||||
setScore(player, bucketId, getScore(player, bucketId) + amount);
|
||||
}
|
||||
|
||||
public void archiveScore(Player player) {
|
||||
@@ -93,20 +106,77 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
}
|
||||
|
||||
public int getScore(Player player) {
|
||||
return getPlayerScore(player);
|
||||
return getScore(player, getPlayerBucket(player));
|
||||
}
|
||||
|
||||
public int getScore(Player player, String bucketId) {
|
||||
ScoreBucket bucket = requireBucket(bucketId);
|
||||
return _bucketScores
|
||||
.get(bucketId)
|
||||
.computeIfAbsent(player.getUniqueId(), uuid -> bucket.getInitialValue());
|
||||
}
|
||||
|
||||
public void resetScore(Player player) {
|
||||
setPlayerScore(player, 0);
|
||||
resetScore(player, getPlayerBucket(player));
|
||||
}
|
||||
|
||||
public void resetScore(Player player, String bucketId) {
|
||||
setScore(player, bucketId, requireBucket(bucketId).getInitialValue());
|
||||
}
|
||||
|
||||
public void setScore(Player player, int score) {
|
||||
setPlayerScore(player, score);
|
||||
setScore(player, getPlayerBucket(player), score);
|
||||
}
|
||||
|
||||
public void setScore(Player player, String bucketId, int score) {
|
||||
requireBucket(bucketId);
|
||||
_bucketScores.get(bucketId).put(player.getUniqueId(), score);
|
||||
}
|
||||
|
||||
public void subtractScore(Player player, int amount) {
|
||||
int oldScore = getPlayerScore(player);
|
||||
setPlayerScore(player, oldScore - amount);
|
||||
subtractScore(player, getPlayerBucket(player), amount);
|
||||
}
|
||||
|
||||
public void subtractScore(Player player, String bucketId, int amount) {
|
||||
setScore(player, bucketId, getScore(player, bucketId) - amount);
|
||||
}
|
||||
|
||||
public ScoreBucket createBucket(String id, String singular, String plural, int initialValue) {
|
||||
ScoreBucket bucket = new ScoreBucket(id, singular, plural, initialValue);
|
||||
if (_buckets.containsKey(id)) {
|
||||
throw new IllegalArgumentException("A score bucket with ID " + id + " already exists");
|
||||
}
|
||||
_buckets.put(id, bucket);
|
||||
_bucketScores.put(id, new LinkedHashMap<>());
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public ScoreBucket getBucket(String bucketId) {
|
||||
return _buckets.get(bucketId);
|
||||
}
|
||||
|
||||
public Map<String, ScoreBucket> getBuckets() {
|
||||
return Map.copyOf(_buckets);
|
||||
}
|
||||
|
||||
public String getPlayerBucket(Player player) {
|
||||
return _playerBuckets.getOrDefault(player.getUniqueId(), _defaultBucketId);
|
||||
}
|
||||
|
||||
public void switchPlayerBucket(Player player, String bucketId) {
|
||||
requireBucket(bucketId);
|
||||
UUID playerId = player.getUniqueId();
|
||||
if (bucketId.equals(_defaultBucketId)) {
|
||||
_playerBuckets.remove(playerId);
|
||||
} else {
|
||||
_playerBuckets.put(playerId, bucketId);
|
||||
}
|
||||
}
|
||||
|
||||
public void switchAllPlayers(String bucketId) {
|
||||
requireBucket(bucketId);
|
||||
_defaultBucketId = bucketId;
|
||||
_playerBuckets.clear();
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -116,16 +186,84 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
reciever.sendMessage(_messagePrefix.append(message));
|
||||
}
|
||||
|
||||
private int getPlayerScore(Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
if (!_playerScores.containsKey(uuid)) {
|
||||
_playerScores.put(uuid, 0);
|
||||
private ScoreBucket requireBucket(String bucketId) {
|
||||
ScoreBucket bucket = _buckets.get(bucketId);
|
||||
if (bucket == null) {
|
||||
throw new IllegalArgumentException("Unknown score bucket: " + bucketId);
|
||||
}
|
||||
return _playerScores.get(uuid);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
private void setPlayerScore(Player player, int score) {
|
||||
_playerScores.put(player.getUniqueId(), score);
|
||||
private void loadScores() {
|
||||
File scoreFile = new File(getDataFolder(), "scores.yml");
|
||||
if (!scoreFile.isFile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfiguration scores = YamlConfiguration.loadConfiguration(scoreFile);
|
||||
ConfigurationSection bucketSection = scores.getConfigurationSection("buckets");
|
||||
if (bucketSection == null) {
|
||||
loadLegacyScores(scores);
|
||||
return;
|
||||
}
|
||||
|
||||
_buckets.clear();
|
||||
_bucketScores.clear();
|
||||
for (String id : bucketSection.getKeys(false)) {
|
||||
try {
|
||||
createBucket(
|
||||
id,
|
||||
bucketSection.getString(id + ".singular", id),
|
||||
bucketSection.getString(id + ".plural", id),
|
||||
bucketSection.getInt(id + ".initial-value"));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logWarning("Ignoring invalid score bucket " + id + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
if (!_buckets.containsKey("points")) {
|
||||
createBucket("points", "point", "points", 0);
|
||||
}
|
||||
_defaultBucketId = scores.getString("default-bucket", "points");
|
||||
if (!_buckets.containsKey(_defaultBucketId)) {
|
||||
logWarning("Unknown default score bucket; using points instead.");
|
||||
_defaultBucketId = "points";
|
||||
}
|
||||
|
||||
for (String id : _buckets.keySet()) {
|
||||
ConfigurationSection bucketScores =
|
||||
scores.getConfigurationSection("buckets." + id + ".scores");
|
||||
if (bucketScores != null) {
|
||||
loadPlayerScores(bucketScores, _bucketScores.get(id));
|
||||
}
|
||||
}
|
||||
ConfigurationSection playerBuckets = scores.getConfigurationSection("player-buckets");
|
||||
if (playerBuckets != null) {
|
||||
for (String key : playerBuckets.getKeys(false)) {
|
||||
try {
|
||||
UUID playerId = UUID.fromString(key);
|
||||
String bucketId = playerBuckets.getString(key);
|
||||
if (_buckets.containsKey(bucketId) && !bucketId.equals(_defaultBucketId)) {
|
||||
_playerBuckets.put(playerId, bucketId);
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logWarning("Ignoring invalid player bucket UUID: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLegacyScores(YamlConfiguration scores) {
|
||||
loadPlayerScores(scores, _bucketScores.get("points"));
|
||||
}
|
||||
|
||||
private void loadPlayerScores(ConfigurationSection scores, Map<UUID, Integer> target) {
|
||||
for (String key : scores.getKeys(false)) {
|
||||
try {
|
||||
target.put(UUID.fromString(key), scores.getInt(key));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logWarning("Ignoring score with invalid player UUID: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
This file is part of ScoreKeeper.
|
||||
|
||||
ScoreKeeper is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ScoreKeeper is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with ScoreKeeper. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
*/
|
||||
|
||||
package com.majinnaibu.minecraft.plugins.scorekeeper.commands;
|
||||
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.ScoreBucket;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.ScoreKeeperPlugin;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ScoreBucketCommand implements CommandExecutor {
|
||||
private final ScoreKeeperPlugin _plugin;
|
||||
|
||||
public ScoreBucketCommand(ScoreKeeperPlugin plugin) {
|
||||
_plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (args.length == 0) {
|
||||
if (sender instanceof Player player) {
|
||||
String bucketId = _plugin.getPlayerBucket(player);
|
||||
ScoreBucket bucket = _plugin.getBucket(bucketId);
|
||||
_plugin.sendMessage(
|
||||
sender,
|
||||
Component.text(
|
||||
"You are tracking " + bucket.getId() + " (" + bucket.getPlural() + ")."));
|
||||
} else {
|
||||
sendUsage(sender);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 5 && args[0].equalsIgnoreCase("create")) {
|
||||
if (!requireAdmin(sender)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
_plugin.createBucket(args[1], args[2], args[3], Integer.parseInt(args[4]));
|
||||
_plugin.sendMessage(sender, Component.text("Created score bucket " + args[1] + "."));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sendError(sender, ex.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 1 && sender instanceof Player player) {
|
||||
switchPlayerBucket(sender, player, args[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
if (!requireAdmin(sender)) {
|
||||
return true;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("all")) {
|
||||
try {
|
||||
_plugin.switchAllPlayers(args[1]);
|
||||
_plugin.sendMessage(
|
||||
sender, Component.text("All players are now tracking " + args[1] + "."));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sendError(sender, ex.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = _plugin.getServer().getPlayerExact(args[0]);
|
||||
if (target == null) {
|
||||
sendError(sender, "Can't find a player with that name");
|
||||
} else {
|
||||
switchPlayerBucket(sender, target, args[1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sendUsage(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void switchPlayerBucket(CommandSender sender, Player target, String bucketId) {
|
||||
try {
|
||||
_plugin.switchPlayerBucket(target, bucketId);
|
||||
_plugin.sendMessage(
|
||||
sender, Component.text(target.getName() + " is now tracking " + bucketId + "."));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sendError(sender, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requireAdmin(CommandSender sender) {
|
||||
if (sender.hasPermission("scorekeeper.admin")) {
|
||||
return true;
|
||||
}
|
||||
sendError(sender, "You do not have permission to manage score buckets");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void sendError(CommandSender sender, String message) {
|
||||
_plugin.sendMessage(sender, Component.text(message).color(NamedTextColor.RED));
|
||||
}
|
||||
|
||||
private void sendUsage(CommandSender sender) {
|
||||
_plugin.sendMessage(
|
||||
sender,
|
||||
Component.text(
|
||||
"Usage: /score-bucket [bucketId] | create <id> <singular> <plural> <initialValue>"
|
||||
+ " | <player|all> <bucketId>")
|
||||
.color(NamedTextColor.YELLOW));
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,11 @@ commands:
|
||||
score-archive:
|
||||
description: Archives and resets the player's score
|
||||
usage: /score-archive [playername]
|
||||
score-bucket:
|
||||
description: Selects or manages score buckets.
|
||||
usage: /score-bucket [bucketId] | create <id> <singular> <plural> <initialValue> | <player|all> <bucketId>
|
||||
permissions:
|
||||
scorekeeper.admin:
|
||||
description: Allows managing score buckets.
|
||||
default: op
|
||||
|
||||
Reference in New Issue
Block a user