Create EggController, small UX/logging fixes

Move controller-related Egg functionality to EggController
Re-add egg stolen message
Logging changes
Don't remove the egg from players in the void, wait for them to die
This commit is contained in:
J0nasL 2023-07-18 14:18:30 -04:00
parent 5f2038e97b
commit eaf1d01660
8 changed files with 275 additions and 167 deletions

View file

@ -0,0 +1,139 @@
package io.github.J0hnL0cke.egghunt.Controller;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import io.github.J0hnL0cke.egghunt.Model.Configuration;
import io.github.J0hnL0cke.egghunt.Model.Data;
import io.github.J0hnL0cke.egghunt.Model.LogHandler;
/**
* Provides functionality relating to the dragon egg, but does not register any event handlers.
* This is a pure fabrication class to simplify functionality used by multiple controllers.
*/
public class EggController {
/**
* Makes the given entity invulnerable if enabled in the config
*/
public static void makeEggInvulnerable(Entity entity, Configuration config) {
if (config.getEggInvulnerable()) {
entity.setInvulnerable(true);
}
}
/**
* Alerts when the egg is destroyed and respawns it if needed
*/
public static void eggDestroyed(Configuration config, Data data, LogHandler logger) {
logger.log("Egg was destroyed");
OfflinePlayer oldOwner = data.getEggOwner();
data.resetEggOwner(false, config);
data.resetEggLocation();
String msg;
if (config.getRespawnEgg()) {
if (config.getRespawnImmediately()) {
logger.log("Immediate respawn enabled- respawning egg");
respawnEgg(config, data, logger);
msg = "The dragon egg was destroyed and has respawned in The End!";
if (oldOwner != null) { //prevent spamming of egg destruction
}
} else {
logger.log("Immediate respawn disabled- egg will respawn after next dragon fight");
msg = "The dragon egg has been destroyed! It will respawn the next time the Ender Dragon is defeated.";
}
} else {
logger.log("Egg respawn is disabled");
msg = "The dragon egg has been destroyed!";
}
Announcement.announce(msg, logger);
}
/**
* Returns the location above the end fountain where the dragon will respawn
*/
public static Location getEggRespawnLocation(Configuration config) {
//the block above the bedrock fountain where the egg spawns
return config.getEndWorld().getEnderDragonBattle().getEndPortalLocation().add(0, 4, 0);
}
/**
* Drop the egg out of the given player's inventory
*/
public static void dropEgg(Player player, Data data, Configuration config) {
// Check if the player has a dragon egg
if (player.getInventory().contains(Material.DRAGON_EGG)) {
// Set owner and remove
data.setEggOwner(player, config); //TODO is this necessary? player will likely already be owner
player.getInventory().remove(Material.DRAGON_EGG);
// Drop it on the floor and set its location
//TODO use drop egg method in EggRespawn
Item eggDrop = player.getWorld().dropItem(player.getLocation(),
new ItemStack(Material.DRAGON_EGG));
eggDrop.setVelocity(new Vector(0, 0, 0));
data.updateEggLocation(eggDrop);
}
}
/**
* Updates the egg ownership scoreboard tag for the given player if tagging is enabled
* Adds the tag if the player is the owner, otherwise removes it
*/
public static void updateOwnerTag(Player player, Data data, Configuration config) {
if (player != null) {
if (config.getTagOwner()) {
OfflinePlayer owner = data.getEggOwner();
//if the given player owns the egg
if (owner != null && owner.getUniqueId().equals(player.getUniqueId())) {
player.addScoreboardTag(config.getOwnerTagName());
} else {
player.removeScoreboardTag(config.getOwnerTagName());
}
}
}
}
/**
* Spawns a new egg item at the given location, sets it to invincible if enabled in the given config.
* This should trigger the item drop event, so it should not be necessary to immediately update the data file with the returned item.
* @return the egg item that was spawned
*/
public static Item spawnEggItem(Location loc, Configuration config, Data data) {
ItemStack egg = new ItemStack(Material.DRAGON_EGG);
egg.setAmount(1);
Item drop = loc.getWorld().dropItem(loc, egg);
drop.setGravity(false);
drop.setGlowing(true);
drop.setVelocity(new Vector().setX(0).setY(0).setZ(0));
if (config.getEggInvulnerable()) {
drop.setInvulnerable(true);
}
return drop;
}
/**
* Respawns the dragon egg in the end
*/
public static void respawnEgg(Configuration config, Data data, LogHandler logger) {
logger.log("Respawning egg");
Block eggBlock = getEggRespawnLocation(config).getBlock();
eggBlock.setType(Material.DRAGON_EGG);
data.updateEggLocation(eggBlock);
Announcement.ShowEggEffects(eggBlock.getLocation().add(0.5, 0, 0.5)); //target the center bottom of the block
}
}

View file

@ -56,7 +56,7 @@ public class EggDestroyListener implements Listener {
if (Egg.hasEgg(item)) {
//make sure item is destroyed to prevent dupes
event.getEntity().remove();
Egg.eggDestroyed(config, data, logger);
EggController.eggDestroyed(config, data, logger);
}
}
}
@ -75,7 +75,7 @@ public class EggDestroyListener implements Listener {
log("canceled explosion of egg item frame");
event.setCancelled(true);
} else {
Egg.eggDestroyed(config, data, logger);
EggController.eggDestroyed(config, data, logger);
frame.setItem(null); //make sure item is removed
}
}
@ -94,7 +94,7 @@ public class EggDestroyListener implements Listener {
if (Egg.hasEgg((ItemStack) event.getEntity())) {
//remove just in case to prevent dupes
event.getEntity().remove();
Egg.eggDestroyed(config, data, logger);
EggController.eggDestroyed(config, data, logger);
}
}
@ -105,7 +105,7 @@ public class EggDestroyListener implements Listener {
//if the egg does not exist
if (config.getEndWorld().getEnderDragonBattle().hasBeenPreviouslyKilled()) {
//if the dragon has already been beaten
Egg.respawnEgg(config, data, logger);
EggController.respawnEgg(config, data, logger);
Announcement.announce("The dragon egg has spawned in the end!", logger);
}
}
@ -158,7 +158,7 @@ public class EggDestroyListener implements Listener {
for (Block egg : eggs) {
if (Egg.isOnlyEgg(egg)) {
if (Egg.getEggRespawnLocation(config).getBlock().equals(egg)) {
if (EggController.getEggRespawnLocation(config).getBlock().equals(egg)) {
//if the egg is set to respawn, and is overlapped by the dragon at the respawn point,
//preserve it rather than letting it be deleted
data.resetEggOwner(false, config);
@ -210,7 +210,7 @@ public class EggDestroyListener implements Listener {
Egg.removeEgg(b); //delete egg
}
log("Dragon egg was replaced");
Egg.eggDestroyed(config, data, logger);
EggController.eggDestroyed(config, data, logger);
}
}
}
@ -241,7 +241,7 @@ public class EggDestroyListener implements Listener {
Egg.removeEgg(b); //delete egg
}
log("Dragon egg was replaced");
Egg.eggDestroyed(config, data, logger);
EggController.eggDestroyed(config, data, logger);
}
}
}
@ -263,10 +263,6 @@ public class EggDestroyListener implements Listener {
}
}
private void announce(String msg) {
Announcement.announce(msg, logger);
}
private void log(String message) {
logger.log(message);
}

View file

@ -42,12 +42,12 @@ public class EventScheduler extends BukkitRunnable {
logger.warning("Resetting egg location to prevent repeated warnings");
data.resetEggLocation();
return;
} else if (isUnderWorld(data.getEggEntity())) {
} else if (isUnderWorld(data.getEggEntity()) && !(data.getEggEntity() instanceof Player)) {
logger.log("Egg entity is under the word. Removing egg from entity");
Location respawnLoc = data.getEggEntity().getLocation();
Egg.removeEgg(data.getEggEntity());
if (config.getEggInvulnerable()) {
if (config.getEggInvulnerable()) {
// get coords for egg to spawn at
//get highest block
Block highestBlock = respawnLoc.getWorld().getHighestBlockAt(respawnLoc);
@ -58,12 +58,13 @@ public class EventScheduler extends BukkitRunnable {
yPos = respawnLoc.getWorld().getSeaLevel();
}
respawnLoc.setY(yPos);
Egg.spawnEggItem(respawnLoc, config, data); //do not need to update data with this location since item spawn event will be called
EggController.spawnEggItem(respawnLoc, config, data); //do not need to update data with this location since item spawn event will be called
data.resetEggOwner(true, config);
} else {
//alert and respawn if applicable
Egg.eggDestroyed(config, data, logger);
EggController.eggDestroyed(config, data, logger);
}
data.resetEggOwner(true, config);
}
}

View file

@ -56,7 +56,7 @@ public class InventoryListener implements Listener {
if (Egg.hasEgg(stack)) {
data.setEggOwner(event.getPlayer(), config);
data.updateEggLocation(item);
Egg.makeEggInvulnerable(event.getItemDrop(), config);
EggController.makeEggInvulnerable(event.getItemDrop(), config);
}
}

View file

@ -54,7 +54,7 @@ public class MiscListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
Egg.updateOwnerTag(player, data, config);
EggController.updateOwnerTag(player, data, config);
}
/**
@ -64,7 +64,7 @@ public class MiscListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
Egg.dropEgg(player, data, config);
EggController.dropEgg(player, data, config);
}
/**
@ -78,7 +78,7 @@ public class MiscListener implements Listener {
if (Egg.hasEgg(item)) {
data.updateEggLocation(item);
Egg.makeEggInvulnerable(event.getEntity(), config);
EggController.makeEggInvulnerable(event.getEntity(), config);
log("made entity invulnerable");
}
}

View file

@ -13,6 +13,7 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import io.github.J0hnL0cke.egghunt.Controller.Announcement;
import io.github.J0hnL0cke.egghunt.Controller.EggController;
import io.github.J0hnL0cke.egghunt.Persistence.DataFileDAO;
/**
@ -37,6 +38,8 @@ public class Data {
private Entity entity;
private UUID entityFallback;
private static final String BUG_STR = "THIS MAY BE A BUG! Please report it at https://github.com/HyperSMP/EggHuntPlugin/issues";
public Data(DataFileDAO dataDao, LogHandler logger) {
this.dataDao = dataDao;
@ -144,6 +147,59 @@ public class Data {
return UUID.fromString(uuid);
}
private void validateData() {
logger.log("Data loaded. Validating loaded data...");
//make sure data was properly loaded
switch (storedAs) {
case BLOCK:
if (block == null) {
logger.warning("Could not locate egg block!");
resetEggLocation();
} else if (!Egg.hasEgg(block)) {
logger.warning("Saved egg block does not actually contain the egg! Was the egg moved while the plugin was disabled?");
logger.warning(BUG_STR);
} else {
log("Successfully found dragon egg block.");
}
break;
case ENTITY:
if (entity == null) {
logger.warning("Could not locate the egg entity!");
resetEggLocation();
} else if (!Egg.hasEgg(entity)) {
logger.warning("Saved egg entity does not actually contain the egg! Was the egg moved while the plugin was disabled?");
logger.warning(BUG_STR);
} else {
log("Successfully found dragon egg entity.");
}
break;
case DNE:
log("Dragon egg has not been claimed.");
}
if (owner != null) {
OfflinePlayer p = Bukkit.getOfflinePlayer(owner);
if (p == null) {
logger.warning("Could not find player with the saved UUID! Resetting egg owner to prevent errors.");
logger.warning(BUG_STR);
resetEggOwner(false, null);
}
}
if (entity == null && storedAs.equals(Egg_Storage_Type.ENTITY)) {
logger.warning("Could not locate the egg entity!");
resetEggLocation();
} else if (block == null && storedAs.equals(Egg_Storage_Type.BLOCK)) {
logger.warning("Could not locate egg block!");
resetEggLocation();
} else if (storedAs.equals(Egg_Storage_Type.DNE)) {
log("Dragon egg has not been claimed.");
} else {
log("Successfully found dragon egg.");
}
}
public void loadData() {
owner = dataDao.readUUID("owner", null);
block = deserializeBlock(dataDao.readLocation("block", null));
@ -160,18 +216,7 @@ public class Data {
} else {
storedAs = Egg_Storage_Type.valueOf(storageString);
//make sure data was properly loaded
if (entity == null && storedAs.equals(Egg_Storage_Type.ENTITY)) {
logger.warning("Could not locate the egg entity!");
resetEggLocation();
} else if (block == null && storedAs.equals(Egg_Storage_Type.BLOCK)) {
logger.warning("Could not locate egg block!");
resetEggLocation();
} else if (storedAs.equals(Egg_Storage_Type.DNE)) {
log("Dragon egg has not been claimed.");
} else {
log("Successfully found dragon egg.");
}
validateData();
}
}
@ -201,18 +246,25 @@ public class Data {
if (!playerUUID.equals(owner)) { //only update if the egg has actually changed posession
UUID oldOwner = owner;
owner = playerUUID; //set new owner
String ownerName = Bukkit.getOfflinePlayer(owner).getName();
String msg;
//if the old owner is online, remove their scoreboard tag
if (oldOwner != null) {
Egg.updateOwnerTag(Bukkit.getPlayer(oldOwner), this, config);
EggController.updateOwnerTag(Bukkit.getPlayer(oldOwner), this, config);
String oldOwnerName = Bukkit.getOfflinePlayer(oldOwner).getName();
msg = String.format("%s has stolen the dragon egg from %s!", ownerName, oldOwnerName);
} else {
msg = String.format("%s has claimed the dragon egg!", ownerName);
}
Announcement.announce(
String.format("%s has claimed the dragon egg!", Bukkit.getOfflinePlayer(owner).getName()), logger);
Announcement.announce(msg, logger);
Player p = Bukkit.getPlayer(playerUUID);
if (p != null) { //make sure player is online
Announcement.ShowEggEffects(p);
Egg.updateOwnerTag(p, this, config); //update the scoreboard tag of the new owner
EggController.updateOwnerTag(p, this, config); //update the scoreboard tag of the new owner
}
}
}
@ -225,14 +277,16 @@ public class Data {
Announcement.announce(String.format("%s no longer owns the dragon egg", ownerName), logger);
}
log("Egg owner has been reset");
log("Egg owner reset");
owner = null;
Egg.updateOwnerTag(oldOwner, this, config); //update tag after setting owner to null
if (config != null) {
EggController.updateOwnerTag(oldOwner, this, config); //update tag after setting owner to null
}
}
}
public void resetEggLocation() {
log("Egg no longer exists");
log("Egg location reset");
block = null;
entity = null;
storedAs = Egg_Storage_Type.DNE;

View file

@ -1,8 +1,6 @@
package io.github.J0hnL0cke.egghunt.Model;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
@ -11,136 +9,24 @@ import org.bukkit.block.ShulkerBox;
import org.bukkit.entity.Entity;
import org.bukkit.entity.FallingBlock;
import org.bukkit.entity.Item;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.BundleMeta;
import org.bukkit.util.Vector;
import io.github.J0hnL0cke.egghunt.Controller.Announcement;
/**
* Provides methods related to the dragon egg
* Provides methods related to checking for or removing the dragon egg
*/
public class Egg {
private static Material egg = Material.DRAGON_EGG;
/**
* Makes the given entity invulnerable if enabled in the config
*/
public static void makeEggInvulnerable(Entity entity, Configuration config) {
if (config.getEggInvulnerable()) {
entity.setInvulnerable(true);
}
}
/**
* Alerts when the egg is destroyed and respawns it if needed
*/
public static void eggDestroyed(Configuration config, Data data, LogHandler logger) {
logger.log("egg was destroyed");
OfflinePlayer oldOwner = data.getEggOwner();
data.resetEggOwner(false, config);
data.resetEggLocation();
String msg;
if (config.getRespawnEgg()) {
if (config.getRespawnImmediately()) {
Egg.respawnEgg(config, data, logger);
msg = "The dragon egg has been destroyed and has respawned in The End!";
if (oldOwner != null) { //prevent spamming of egg destruction
Announcement.announce(msg, logger);
}
} else {
msg = "The dragon egg has been destroyed! It will respawn the next time the Ender Dragon is defeated.";
Announcement.announce(msg, logger);
}
} else {
msg = "The dragon egg has been destroyed!";
Announcement.announce(msg, logger);
}
}
/**
* Returns the location above the end fountain where the dragon will respawn
*/
public static Location getEggRespawnLocation(Configuration config) {
//the block above the bedrock fountain where the egg spawns
return config.getEndWorld().getEnderDragonBattle().getEndPortalLocation().add(0, 4, 0);
}
/**
* Drop the egg out of the given player's inventory
*/
public static void dropEgg(Player player, Data data, Configuration config) {
// Check if the player has a dragon egg
if (player.getInventory().contains(Material.DRAGON_EGG)) {
// Set owner and remove
data.setEggOwner(player, config); //TODO is this necessary? player will likely already be owner
player.getInventory().remove(Material.DRAGON_EGG);
// Drop it on the floor and set its location
//TODO use drop egg method in EggRespawn
Item eggDrop = player.getWorld().dropItem(player.getLocation(),
new ItemStack(Material.DRAGON_EGG));
eggDrop.setVelocity(new Vector(0, 0, 0));
data.updateEggLocation(eggDrop);
}
}
/**
* Updates the egg ownership scoreboard tag for the given player if tagging is enabled
* Adds the tag if the player is the owner, otherwise removes it
*/
public static void updateOwnerTag(Player player, Data data, Configuration config) {
if (player != null) {
if (config.getTagOwner()) {
OfflinePlayer owner = data.getEggOwner();
//if the given player owns the egg
if (owner != null && owner.getUniqueId().equals(player.getUniqueId())) {
player.addScoreboardTag(config.getOwnerTagName());
} else {
player.removeScoreboardTag(config.getOwnerTagName());
}
}
}
}
/**
* Spawns a new egg item at the given location, sets it to invincible if enabled in the given config.
* This should trigger the item drop event, so it should not be necessary to immediately update the data file with the returned item.
* @return the egg item that was spawned
*/
public static Item spawnEggItem(Location loc, Configuration config, Data data) {
ItemStack egg = new ItemStack(Material.DRAGON_EGG);
egg.setAmount(1);
Item drop = loc.getWorld().dropItem(loc, egg);
drop.setGravity(false);
drop.setGlowing(true);
drop.setVelocity(new Vector().setX(0).setY(0).setZ(0));
if (config.getEggInvulnerable()) {
drop.setInvulnerable(true);
}
return drop;
}
/**
* Respawns the dragon egg in the end
*/
public static void respawnEgg(Configuration config, Data data, LogHandler logger) {
Block eggBlock = getEggRespawnLocation(config).getBlock();
eggBlock.setType(Material.DRAGON_EGG);
data.updateEggLocation(eggBlock);
Announcement.ShowEggEffects(eggBlock.getLocation().add(0.5, 0, 0.5)); //target the center bottom of the block
}
/**
* Checks if the given ItemStack is a container that is holding the dragon egg. Also returns false if the provided stack is null.
* @param stack ItemStack to check
@ -194,21 +80,35 @@ public class Egg {
* @param entity
* @return True if the entity is holding the dragon egg or a container that is holding the egg, otherwise false
*/
public boolean hasEgg(Entity entity) {
public static boolean hasEgg(Entity entity) {
if (entity instanceof Player) {
return Egg.hasEgg(((Player) entity).getInventory());
} else if (entity instanceof LivingEntity) {
return Egg.hasEgg(((Player) entity).getInventory()); //Handles entire player check, including inventory, equipment, and custom slots
}
//Some entities can be instances of both LivingEntity and InventoryHolder (villager, allay, etc)
//therefore, check both inventory and equipment separately for the egg
if (entity instanceof LivingEntity) {
LivingEntity mob = (((LivingEntity) entity));
EntityEquipment inv = mob.getEquipment();
if (Egg.hasEgg(inv.getItemInMainHand())) {
return true;
} else if (Egg.hasEgg(inv.getItemInOffHand())) {
for (ItemStack item : inv.getArmorContents()) {
if (Egg.hasEgg(item)) {
return true;
}
} else if (entity instanceof FallingBlock) {
}
}
if (entity instanceof InventoryHolder) {
if (Egg.hasEgg(((InventoryHolder) entity).getInventory())) {
return true;
}
}
if (entity instanceof FallingBlock) {
return Egg.hasEgg(((FallingBlock) entity));
} else if (entity instanceof Item) {
return Egg.hasEgg(((Item) entity));
} else if (entity instanceof ItemFrame) {
return Egg.hasEgg(((ItemFrame) entity).getItem());
}
return false;
}
@ -289,13 +189,31 @@ public class Egg {
return false;
}
for (ItemStack stack : inventory.getContents()) {
if (hasEgg(stack)){
if (hasEgg(stack)) {
return true;
}
}
return false;
}
/**
* Check if the given PlayerInventory contains the dragon egg or a container holding the egg. Also returns false if the provided inventory is null.
* Note: Player inventories can contain extra content slots on top of normal inventory contents
* @param inventory PlayerInventory to check
* @return True if the inventory contains the a dragon egg item or an item holding the egg, otherwise false
*/
public static boolean hasEgg(PlayerInventory inventory) {
if (inventory == null) {
return false;
}
for (ItemStack stack : inventory.getExtraContents()) {
if (hasEgg(stack)){
return true;
}
}
return hasEgg((Inventory)inventory);
}
/**
* Check if the given Item is the dragon egg or is holding the egg. Also returns false if the provided item is null.
* @param item Item to check

View file

@ -10,13 +10,13 @@ import org.bukkit.scheduler.BukkitTask;
import io.github.J0hnL0cke.egghunt.Controller.Announcement;
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.EventScheduler;
import io.github.J0hnL0cke.egghunt.Controller.InventoryListener;
import io.github.J0hnL0cke.egghunt.Model.Configuration;
import io.github.J0hnL0cke.egghunt.Model.Data;
import io.github.J0hnL0cke.egghunt.Model.Egg;
import io.github.J0hnL0cke.egghunt.Model.LogHandler;
import io.github.J0hnL0cke.egghunt.Persistence.ConfigFileDAO;
import io.github.J0hnL0cke.egghunt.Persistence.DataFileDAO;
@ -77,7 +77,7 @@ public final class egghunt extends JavaPlugin {
if (eggHolder instanceof Player) {
logger.log("Egg is held by a player, dropping egg...");
Player p = (Player) eggHolder;
Egg.dropEgg(p, data, config);
EggController.dropEgg(p, data, config);
//in case the server isn't restarting, let the player know what happend
Announcement.sendMessage(p, "The dragon egg was dropped from your inventory");
}