Add configurable score change reporting
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
* `/score-reset [playerName]` - Resets the player's score to 0.
|
||||
* `/score-archive [playerName]` - Saves the player's score to a "High Scores" table and resets their current score to 0.
|
||||
* `/score-bucket [bucketId]` - Switches your active bucket.
|
||||
* `/score-bucket create <id> <singular> <plural> <initialValue>` - Creates a bucket (operators).
|
||||
* `/score-bucket create <id> <singular> <plural> <initialValue> [admin|global|player|none]` - Creates a bucket (operators).
|
||||
* `/score-bucket <playerName|all> <bucketId>` - Switches a player or the server default (operators).
|
||||
|
||||
NOTES:
|
||||
@@ -18,7 +18,7 @@ NOTES:
|
||||
* Permissions support is coming AFTER I get archive to work.
|
||||
* The commands aside from get are intended for admins and whatnot as some other plugin should be using the methods instead of letting player's execute commands.
|
||||
|
||||
Other plugins can create buckets with `createBucket(id, singular, plural, initialValue)` and use the bucket-aware score methods. Existing score methods operate on the player's active bucket. Bucket definitions, assignments, and scores are saved in `plugins/ScoreKeeper/scores.yml`.
|
||||
Bucket reporting defaults to `none`; `admin` notifies operators and score admins, `global` notifies all online players, and `player` notifies only the scored player. Other plugins can create buckets with `createBucket(id, singular, plural, initialValue, reporting)` and use the bucket-aware score methods. Existing score methods operate on the player's active bucket. Bucket definitions, reporting policies, assignments, and scores are saved in `plugins/ScoreKeeper/scores.yml`.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
2
TODO.md
2
TODO.md
@@ -22,7 +22,7 @@
|
||||
|:-----:|:--------:|:------|
|
||||
| SK-01 | Done | Save scores between server launches. |
|
||||
| SK-02 | Done | Allow tracking scores by bucket. An admin or other plugin should be able to create types of scores with named other than points. { id: "Bread", singular: "loaf", plural: "loaves", initialValue: 0 } Each player's score will be tracked in a bucket by id. Players may be all switched to a bucket or individually switched. |\
|
||||
| SK-03 | Ready | Add optional score reporting to buckets with these four values. { id: "Bread", singular: "loaf", plural: "loaves", initialValue: 0, reporting:"admin|global|player|none" } admin means ops/admin only, global means everyone on the server, player means only tell the player, and none means don't report changes.
|
||||
| SK-03 | Done | Add optional score reporting to buckets with these four values. { id: "Bread", singular: "loaf", plural: "loaves", initialValue: 0, reporting:"admin|global|player|none" } admin means ops/admin only, global means everyone on the server, player means only tell the player, and none means don't report changes.
|
||||
| SK-04 | Ready | Tell a player when their bucket changes. Something like "You are now tracking ${id}. You have ${score} ${plural or singular depending in score}." |
|
||||
| SK-05 | Ready | Enable high score runs for a bucket. Admins or pugins should be able to start a run on a certain kind of score. The top n (configurable) players will have their scores reported globally for this bucket and the individual player will have their score reported to them when it changes. Scores will be optionally reset to their initialValue with the old score for this bucket saved. High score runs will have a lifetime of a target high score, a time like 4 hours or 1 week, or until-disabled. When the high score run is finished all players will be messaged a table with the top n places and their scores along with the individual user's place and score. The scores will be reset to their previous values if they were reset to their initialValue at the beginning otherwise they will be left alone. |
|
||||
| SK-06 | Ready | Score archiving. Scores either all or within a given bucket should be able to be archived and reset to their initial value. The archived scores should be able to be listed via a command. |
|
||||
|
||||
@@ -22,8 +22,14 @@ public final class ScoreBucket {
|
||||
private final String _singular;
|
||||
private final String _plural;
|
||||
private final int _initialValue;
|
||||
private final ScoreReporting _reporting;
|
||||
|
||||
public ScoreBucket(String id, String singular, String plural, int initialValue) {
|
||||
this(id, singular, plural, initialValue, ScoreReporting.NONE);
|
||||
}
|
||||
|
||||
public ScoreBucket(
|
||||
String id, String singular, String plural, int initialValue, ScoreReporting reporting) {
|
||||
if (id == null || !id.matches("[A-Za-z0-9_-]+")) {
|
||||
throw new IllegalArgumentException("Bucket ID must contain only letters, numbers, _ or -");
|
||||
}
|
||||
@@ -33,10 +39,14 @@ public final class ScoreBucket {
|
||||
if (plural == null || plural.isBlank()) {
|
||||
throw new IllegalArgumentException("Bucket plural label cannot be blank");
|
||||
}
|
||||
if (reporting == null) {
|
||||
throw new IllegalArgumentException("Bucket reporting policy cannot be null");
|
||||
}
|
||||
_id = id;
|
||||
_singular = singular;
|
||||
_plural = plural;
|
||||
_initialValue = initialValue;
|
||||
_reporting = reporting;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@@ -54,4 +64,8 @@ public final class ScoreBucket {
|
||||
public int getInitialValue() {
|
||||
return _initialValue;
|
||||
}
|
||||
|
||||
public ScoreReporting getReporting() {
|
||||
return _reporting;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
scores.set(path + ".singular", bucket.getSingular());
|
||||
scores.set(path + ".plural", bucket.getPlural());
|
||||
scores.set(path + ".initial-value", bucket.getInitialValue());
|
||||
scores.set(path + ".reporting", bucket.getReporting().name().toLowerCase());
|
||||
for (var entry : _bucketScores.get(bucket.getId()).entrySet()) {
|
||||
scores.set(path + ".scores." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
@@ -129,8 +130,12 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
}
|
||||
|
||||
public void setScore(Player player, String bucketId, int score) {
|
||||
requireBucket(bucketId);
|
||||
ScoreBucket bucket = requireBucket(bucketId);
|
||||
int oldScore = getScore(player, bucketId);
|
||||
_bucketScores.get(bucketId).put(player.getUniqueId(), score);
|
||||
if (oldScore != score) {
|
||||
reportScoreChange(player, bucket, score);
|
||||
}
|
||||
}
|
||||
|
||||
public void subtractScore(Player player, int amount) {
|
||||
@@ -142,7 +147,12 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
}
|
||||
|
||||
public ScoreBucket createBucket(String id, String singular, String plural, int initialValue) {
|
||||
ScoreBucket bucket = new ScoreBucket(id, singular, plural, initialValue);
|
||||
return createBucket(id, singular, plural, initialValue, ScoreReporting.NONE);
|
||||
}
|
||||
|
||||
public ScoreBucket createBucket(
|
||||
String id, String singular, String plural, int initialValue, ScoreReporting reporting) {
|
||||
ScoreBucket bucket = new ScoreBucket(id, singular, plural, initialValue, reporting);
|
||||
if (_buckets.containsKey(id)) {
|
||||
throw new IllegalArgumentException("A score bucket with ID " + id + " already exists");
|
||||
}
|
||||
@@ -211,11 +221,14 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
_bucketScores.clear();
|
||||
for (String id : bucketSection.getKeys(false)) {
|
||||
try {
|
||||
ScoreReporting reporting =
|
||||
parseReporting(bucketSection.getString(id + ".reporting", "none"), id);
|
||||
createBucket(
|
||||
id,
|
||||
bucketSection.getString(id + ".singular", id),
|
||||
bucketSection.getString(id + ".plural", id),
|
||||
bucketSection.getInt(id + ".initial-value"));
|
||||
bucketSection.getInt(id + ".initial-value"),
|
||||
reporting);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logWarning("Ignoring invalid score bucket " + id + ": " + ex.getMessage());
|
||||
}
|
||||
@@ -266,6 +279,41 @@ public class ScoreKeeperPlugin extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private ScoreReporting parseReporting(String value, String bucketId) {
|
||||
try {
|
||||
return ScoreReporting.fromString(value);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logWarning("Ignoring invalid reporting policy for bucket " + bucketId + "; using none.");
|
||||
return ScoreReporting.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private void reportScoreChange(Player player, ScoreBucket bucket, int score) {
|
||||
ScoreReporting reporting = bucket.getReporting();
|
||||
if (reporting == ScoreReporting.NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
String unit = score == 1 ? bucket.getSingular() : bucket.getPlural();
|
||||
String text =
|
||||
player.getName() + "'s " + bucket.getId() + " score is now " + score + " " + unit + ".";
|
||||
Component message = Component.text(text);
|
||||
if (reporting == ScoreReporting.PLAYER) {
|
||||
sendMessage(player, message);
|
||||
} else if (reporting == ScoreReporting.GLOBAL) {
|
||||
for (Player recipient : getServer().getOnlinePlayers()) {
|
||||
sendMessage(recipient, message);
|
||||
}
|
||||
} else if (reporting == ScoreReporting.ADMIN) {
|
||||
logInfo(text);
|
||||
for (Player recipient : getServer().getOnlinePlayers()) {
|
||||
if (recipient.isOp() || recipient.hasPermission("scorekeeper.admin")) {
|
||||
sendMessage(recipient, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Logging
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum ScoreReporting {
|
||||
ADMIN,
|
||||
GLOBAL,
|
||||
PLAYER,
|
||||
NONE;
|
||||
|
||||
public static ScoreReporting fromString(String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Reporting policy cannot be null");
|
||||
}
|
||||
try {
|
||||
return valueOf(value.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IllegalArgumentException("Reporting must be admin, global, player or none", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package com.majinnaibu.minecraft.plugins.scorekeeper.commands;
|
||||
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.ScoreBucket;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.ScoreKeeperPlugin;
|
||||
import com.majinnaibu.minecraft.plugins.scorekeeper.ScoreReporting;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
@@ -51,12 +52,14 @@ public class ScoreBucketCommand implements CommandExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 5 && args[0].equalsIgnoreCase("create")) {
|
||||
if ((args.length == 5 || args.length == 6) && args[0].equalsIgnoreCase("create")) {
|
||||
if (!requireAdmin(sender)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
_plugin.createBucket(args[1], args[2], args[3], Integer.parseInt(args[4]));
|
||||
ScoreReporting reporting =
|
||||
args.length == 6 ? ScoreReporting.fromString(args[5]) : ScoreReporting.NONE;
|
||||
_plugin.createBucket(args[1], args[2], args[3], Integer.parseInt(args[4]), reporting);
|
||||
_plugin.sendMessage(sender, Component.text("Created score bucket " + args[1] + "."));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sendError(sender, ex.getMessage());
|
||||
|
||||
@@ -20,7 +20,7 @@ commands:
|
||||
usage: /score-archive [playername]
|
||||
score-bucket:
|
||||
description: Selects or manages score buckets.
|
||||
usage: /score-bucket [bucketId] | create <id> <singular> <plural> <initialValue> | <player|all> <bucketId>
|
||||
usage: /score-bucket [bucketId] | create <id> <singular> <plural> <initialValue> [admin|global|player|none] | <player|all> <bucketId>
|
||||
permissions:
|
||||
scorekeeper.admin:
|
||||
description: Allows managing score buckets.
|
||||
|
||||
Reference in New Issue
Block a user