Finished scoreboard, added some config settings

This commit is contained in:
J0nasL 2023-07-19 21:28:35 -04:00
parent 2ffb8bbe14
commit ede546e5ed
8 changed files with 187 additions and 149 deletions

View file

@ -29,14 +29,12 @@ public class MiscListener implements Listener {
private LogHandler logger;
private Configuration config;
private Data data;
private ScoreboardHandler scoreboardHandler;
public MiscListener(LogHandler logger, Configuration config, Data data, ScoreboardHandler scoreboardHandler) {
public MiscListener(LogHandler logger, Configuration config, Data data) {
this.logger = logger;
this.config = config;
this.data = data;
this.scoreboardHandler = scoreboardHandler;
}
/**
@ -47,7 +45,6 @@ public class MiscListener implements Listener {
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onAutosave(WorldSaveEvent event) {
scoreboardHandler.updateScores();
data.saveData();
}

View file

@ -0,0 +1,151 @@
package io.github.J0hnL0cke.egghunt.Controller;
import java.time.temporal.ChronoUnit;
import java.time.Instant;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Entity;
import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.RenderType;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.ScoreboardManager;
import io.github.J0hnL0cke.egghunt.Model.Configuration;
import io.github.J0hnL0cke.egghunt.Model.Data;
import io.github.J0hnL0cke.egghunt.Model.LogHandler;
/**
* Handles interactions with the scoreboard api
*/
public class ScoreboardController {
private static ScoreboardController thisHandler;
private Data data;
private Configuration config;
private LogHandler logger;
private ScoreboardManager manager;
private Scoreboard board;
private Objective eggMinutes;
private Objective eggSeconds;
private Instant lastUpdate;
private static final String EGG_MINUTES_KEY = "eggOwnedMinutes";
private static final String EGG_SECONDS_KEY = "eggOwnedSeconds";
public static ScoreboardController getScoreboardHandler(Data data, Configuration configuration, LogHandler logger) {
if (thisHandler == null) {
thisHandler = new ScoreboardController(data, configuration, logger);
}
return thisHandler;
}
private ScoreboardController(Data data, Configuration config, LogHandler logger) {
this.data = data;
this.config = config;
this.logger = logger;
if (config.getKeepScore()) {
logger.log("Scorekeeping is enabled, loading scoreboard...");
loadData();
}
}
public static void saveData(LogHandler logger) {
if (thisHandler == null) {
logger.warning("ScoreboardController has not been initalized before attempting to save!");
} else {
thisHandler.updateScores();
}
}
private void loadData() {
manager = Bukkit.getScoreboardManager();
board = manager.getMainScoreboard(); //possibly switch to new board? would need to save/load independently of server
eggSeconds = getOrMakeObjective(EGG_SECONDS_KEY, "Seconds owning egg");
eggMinutes = getOrMakeObjective(EGG_MINUTES_KEY, "Minutes owning egg");
lastUpdate = Instant.now();
}
/**
* Updates the score for the current egg owner
*
* For accurate scoring, this should be updated at least every time egg ownership changes, and
* every time the plugin is enabled/disabled
* @param data
*/
public void updateScores() {
if (config.getKeepScore() && data.getEggType()!= Data.Egg_Storage_Type.DNE) {
logger.log("Updating scoreboard");
if (data.getEggType() == Data.Egg_Storage_Type.ENTITY) {
Entity eggEntity = data.getEggEntity();
if (config.getNamedEntitiesGetScore()) {
if (eggEntity.getCustomName() != null) {
incrementScoring(eggEntity.getCustomName()); //prioritize the named entity for scoring
return;
}
}
}
OfflinePlayer owner = data.getEggOwner();
if (owner != null) {
incrementScoring(owner.getName());
}
}
}
private void incrementScoring(String entityName) {
//YOU WILL REMEMBER ME! REMEMBER ME FOR
//ChronoUnit.CENTURIES
Instant newUpdate = Instant.now(); //when writing tests, switch this to using an injected Clock instance
long seconds = ChronoUnit.SECONDS.between(lastUpdate, newUpdate); //this is the floored duration in minutes
int convertedSeconds = Long.valueOf(seconds).intValue();
addScore(entityName, eggSeconds, convertedSeconds); //add to eggSeconds
int mins = Math.floorDiv(getScore(entityName, eggSeconds), 60); //convert seconds into minutes
setScore(entityName, eggMinutes, mins); //update eggMinutes
lastUpdate = newUpdate; //update lastUpdate time
}
public Objective getOrMakeObjective(String key, String displayName) {
Objective obj = board.getObjective(key);
if (obj == null) {
logger.info(String.format("No scoreboard objective \"%s\" found, creating new objective", key));
obj = board.registerNewObjective(key, Criteria.DUMMY, displayName, RenderType.INTEGER);
}
if (!obj.isModifiable()) {
String msg = "Could not modify objective \"%s\"! Make sure objective criteria is set to dummy!";
logger.warning(String.format(msg, obj.getName()));
}
return obj;
}
private void addScore(String name, Objective obj, int score) {
if (score != 0) { //avoid adding the entity to the scoreboard if adding 0
Score s = obj.getScore(name);
s.setScore(s.getScore() + score);
}
}
private void setScore(String name, Objective obj, int score) {
Score s = obj.getScore(name);
s.setScore(score);
}
private int getScore(String name, Objective obj) {
Score s = obj.getScore(name);
return s.getScore();
}
}

View file

@ -1,135 +0,0 @@
package io.github.J0hnL0cke.egghunt.Controller;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.time.Instant;
import java.util.Date;
import java.time.temporal.TemporalUnit;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Statistic;
import org.bukkit.entity.Entity;
import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.RenderType;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.ScoreboardManager;
import io.github.J0hnL0cke.egghunt.Model.Configuration;
import io.github.J0hnL0cke.egghunt.Model.Data;
import io.github.J0hnL0cke.egghunt.Model.LogHandler;
/**
* Handles interactions with the scoreboard
*/
public class ScoreboardHandler {
private Data data;
private Configuration config;
private LogHandler logger;
private ScoreboardManager manager;
private Scoreboard board;
private Objective eggObjective;
private Instant lastUpdate;
private static final String OBJECTIVE_KEY = "eggy";
public ScoreboardHandler(Data data, Configuration config, LogHandler logger) {
this.data = data;
this.config = config;
this.logger = logger;
manager = Bukkit.getScoreboardManager();
board = manager.getMainScoreboard(); //possibly switch to new board? would need to save/load independently of server
eggObjective = board.getObjective(OBJECTIVE_KEY);
if (eggObjective == null) {
//TODO
logger.info("No scoreboard objective for owning egg found, creating new objective");
logger.log("TODO");
}
lastUpdate = Instant.now();
}
/**
* Updates the score for the current egg owner
*
* For accurate scoring, this should be updated at least every time egg ownership changes, and
* every time the plugin is enabled/disabled
* @param data
*/
public void updateScores() {
OfflinePlayer owner = data.getEggOwner();
if (owner != null) {
if (data.getEggEntity() != null) {
//TODO remove later, this is just for testing
addScore(data.getEggEntity(), 0);
}
Instant newUpdate = Instant.now(); //when writing tests, switch this to using an injected Clock instance
//YOU WILL REMEMBER ME! REMEMBER ME FOR
//ChronoUnit.CENTURIES
long mins = ChronoUnit.MINUTES.between(lastUpdate, newUpdate); //this is the floored duration in minutes
int minInt = Long.valueOf(mins).intValue();
addScore(data.getEggOwner(), minInt);
lastUpdate = newUpdate;
}
}
public void makeScoreboard() {
Objective newObj = board.registerNewObjective(OBJECTIVE_KEY, Criteria.DUMMY, "Minutes owning egg",
RenderType.INTEGER);
//newObj.isModifiable();
//setScore(null, 0);
//manager.
//Criteria c = Criteria.statistic(Statistic.);
//Criteria d = Criteria.create("MinsHeldEgg");
}
private void addScore(OfflinePlayer p, int score) {
setScore(p, getScore(p) + score); //maybe do this more efficiently
}
private void addScore(Entity e, int score) { //TODO testing method, remove
String key = e.getUniqueId().toString();
if (e.getCustomName() != null) {
key = e.getCustomName();
}
Score s = eggObjective.getScore(key);
s.setScore(s.getScore() + 1);
}
private void setScore(OfflinePlayer p, int score) {
Score s = eggObjective.getScore(p.getName());
s.setScore(score);
}
private int getScore(OfflinePlayer p) {
Score s = eggObjective.getScore(p.getName());
return s.getScore();
}
}

View file

@ -18,6 +18,8 @@ public class Configuration {
private boolean dropEnderchestedEgg; /** whether an existing egg already in an ender chest should be forced out or stuck there */
private boolean canPackageEgg; /** whether the egg can be stored in a shulker box or bundle */
private boolean tagOwner; /** whether to apply a tag to the owner of the egg */
private boolean keepScore; /** whether to create and increment a scoreboard for time holding the egg */
private boolean namedEntitiesGetScore; /** whether entities with custom names are counted on the scoreboard when holding the egg */
private World endWorld; /** the name of the world that counts as the end on this server */
private String ownerTagName; /** the name of the tag to apply to the owner, if enabled */
@ -42,6 +44,8 @@ public class Configuration {
dropEnderchestedEgg = fileDao.readBool("drop_enderchested_egg");
canPackageEgg = fileDao.readBool("can_package_egg");
tagOwner = fileDao.readBool("tag_owner");
keepScore = fileDao.readBool("keep_score");
namedEntitiesGetScore = fileDao.readBool("named_entities_keep_score");
ownerTagName = fileDao.read("owner_tag_name", null);
endWorld = Bukkit.getServer().getWorld(fileDao.read("end", DEFAULT_END));
@ -79,11 +83,18 @@ public class Configuration {
return false; //canPackageEgg; TODO implement checks for this
}
public boolean getTagOwner() {
return tagOwner;
}
public boolean getKeepScore() {
return keepScore;
}
public boolean getNamedEntitiesGetScore() {
return namedEntitiesGetScore;
}
public String getOwnerTagName(){
return ownerTagName;
}

View file

@ -14,6 +14,7 @@ import org.bukkit.inventory.InventoryHolder;
import io.github.J0hnL0cke.egghunt.Controller.Announcement;
import io.github.J0hnL0cke.egghunt.Controller.EggController;
import io.github.J0hnL0cke.egghunt.Controller.ScoreboardController;
import io.github.J0hnL0cke.egghunt.Persistence.DataFileDAO;
/**
@ -221,6 +222,8 @@ public class Data {
}
public void saveData() {
ScoreboardController.saveData(logger);
dataDao.writeUUID("owner", owner);
dataDao.writeLocation("block", serializeBlock(block));
dataDao.writeUUID("entity", serializeEntity(entity));
@ -244,6 +247,7 @@ public class Data {
private void setEggOwner(UUID playerUUID, Configuration config) {
if (!playerUUID.equals(owner)) { //only update if the egg has actually changed posession
ScoreboardController.saveData(logger);
UUID oldOwner = owner;
owner = playerUUID; //set new owner
String ownerName = Bukkit.getOfflinePlayer(owner).getName();
@ -270,6 +274,7 @@ public class Data {
}
public void resetEggOwner(boolean announce, Configuration config) {
ScoreboardController.saveData(logger);
if (owner != null) {
Player oldOwner = Bukkit.getPlayer(owner);
if (announce) { //TODO move announcements somewhere else?

View file

@ -143,7 +143,9 @@ public class Egg {
if (entity instanceof LivingEntity) {
LivingEntity mob = (((LivingEntity) entity));
EntityEquipment inv = mob.getEquipment();
if (Egg.hasEgg(inv.getArmorContents())) { //TODO check if includes main/offhand
if (Egg.hasEgg(inv.getArmorContents())) {
return true;
} else if (Egg.hasEgg(inv.getItemInMainHand()) || Egg.hasEgg(inv.getItemInOffHand())) {
return true;
}
}

View file

@ -13,7 +13,7 @@ import io.github.J0hnL0cke.egghunt.Controller.CommandHandler;
import io.github.J0hnL0cke.egghunt.Controller.EggController;
import io.github.J0hnL0cke.egghunt.Controller.EggDestroyListener;
import io.github.J0hnL0cke.egghunt.Controller.MiscListener;
import io.github.J0hnL0cke.egghunt.Controller.ScoreboardHandler;
import io.github.J0hnL0cke.egghunt.Controller.ScoreboardController;
import io.github.J0hnL0cke.egghunt.Controller.EventScheduler;
import io.github.J0hnL0cke.egghunt.Controller.InventoryListener;
import io.github.J0hnL0cke.egghunt.Model.Configuration;
@ -46,8 +46,8 @@ public final class egghunt extends JavaPlugin {
logger.setDebug(config.getDebugEnabled());
//create controller instances
ScoreboardHandler scoreboardHandler = new ScoreboardHandler(data, config, logger);
MiscListener miscListener = new MiscListener(logger, config, data, scoreboardHandler);
ScoreboardController scoreboardController = ScoreboardController.getScoreboardHandler(data, config, logger);
MiscListener miscListener = new MiscListener(logger, config, data);
InventoryListener inventoryListener = new InventoryListener(logger, config, data);
EggDestroyListener destroyListener = new EggDestroyListener(logger, config, data);
EventScheduler scheduler = new EventScheduler(config, data, logger);

View file

@ -1,5 +1,5 @@
#If enabled, logs extra debugging information to the console. This information will appear in logs regardless of debug setting.
debug: false
debug: true
#Name of your end world, usually to "world_the_end" on spigot
end: world_the_end
@ -13,16 +13,23 @@ egg_inv: false
resp_egg: true
#Whether the egg respawns immediately (true) or after a new dragon is spawned and killed (false) after the egg is destroyed
resp_imm: false
#Whether to drop any eggs that players have in their ender chest onto the ground.
#Setting this to false will prevent the player from removing that egg from their ender chest.
drop_enderchested_egg: false
#Whether the egg is allowed to be stored in bundles and shulker boxes
#WIP
#can_package_egg: true
#can_package_egg: false
#Whether to apply an entity tag to the egg owner (works like `/tag <playerName> add <tagName>`)
#Tag is updated when egg ownership changes and on player login
tag_owner: true
#The name of the tag to apply (selected like: `/tell @a[tag=<tagName>] <message>`)
#Do not overlap with tag names used by other plugins.
#The name of the tag to apply. Selected like: `/tell @a[tag=<tagName>] <message>`
#If changing this while the egg is claimed, remove the old tag by running `/tag <playerName> remove <oldTagName>`.
owner_tag_name: eggOwner
owner_tag_name: eggOwner
#Whether to create and update a scoreboard for time holding the dragon egg
#Scoreboard is updated when the egg changes ownership and when the world saves
keep_score: true
#Whether entities with custom names are counted on the scoreboard instead of the owner when holding the egg
named_entities_keep_score: false