Added GeoLimit for mobs - world setting.

Adds a world menu for preventing mobs from exiting an island.
Defaults are for flying mobs.

Adds an event that is called when the plugin loads.

Test classes are still to be developed.
This commit is contained in:
tastybento 2018-07-22 16:17:34 -07:00
parent 944ab36488
commit b40023bcf9
10 changed files with 244 additions and 143 deletions

View File

@ -294,12 +294,12 @@ island:
team-join-reset: true
customranks: {}
protection:
# Allow pistons to push outside of the protected area (maybe to make bridges)
allow-piston-push: true
# Restrict Wither and other flying mobs.
# Any flying mobs that exit the island space where they were spawned will be removed.
# Includes blaze and ghast.
restrict-flying-mobs: true
# Geo restrict mobs.
# Mobs that exit the island space where they were spawned will be removed.
geo-limit-settings:
- GHAST
- BAT
- BLAZE
# Invincible visitors. List of damages that will not affect visitors.
# Make list blank if visitors should receive all damages
invincible-visitors:
@ -328,13 +328,6 @@ protection:
- CRAMMING
- VOID
togglePvPCooldown: 0
allowEndermanGriefing: false
endermanDeathDrop: false
allowTNTDamage: false
allowChestDamage: false
allowCreeperDamage: false
allowCreeperGriefing: false
allowMobDamageToItemFrames: false
panel:
close-on-click-outside: true
uniqueId: config

View File

@ -442,6 +442,12 @@ protection:
description: "Toggle use"
name: "Gates"
hint: "No gate use"
GEO_LIMIT_MOBS:
description: |
&eRemove mobs that go
&eoutside protected
&eisland space
name: "Limit mobs to island"
HURT_ANIMALS:
description: "Toggle hurting"
name: "Hurt animals"

View File

@ -1,11 +1,13 @@
package us.tastybento.bskyblock;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import us.tastybento.bskyblock.api.configuration.BSBConfig;
import us.tastybento.bskyblock.api.configuration.WorldSettings;
import us.tastybento.bskyblock.api.events.BSBReadyEvent;
import us.tastybento.bskyblock.api.placeholders.PlaceholderHandler;
import us.tastybento.bskyblock.api.user.Notifier;
import us.tastybento.bskyblock.commands.AdminCommand;
@ -148,6 +150,8 @@ public class BSkyBlock extends JavaPlugin {
instance.log("- Tastybento and Poslovitch, 2017-2018");
instance.log("#############################################");
// Fire plugin ready event
Bukkit.getServer().getPluginManager().callEvent(new BSBReadyEvent());
});
});
}

View File

@ -404,26 +404,12 @@ public class Settings implements DataObject, WorldSettings {
// ---------------------------------------------
/* PROTECTION */
@ConfigComment("Allow pistons to push outside of the protected area (maybe to make bridges)")
@ConfigEntry(path = "protection.allow-piston-push")
private boolean allowPistonPush = false;
@ConfigComment("Restrict Wither and other flying mobs.")
@ConfigComment("Any flying mobs that exit the island space where they were spawned will be removed.")
@ConfigComment("Includes blaze and ghast. ")
@ConfigEntry(path = "protection.restrict-flying-mobs")
private boolean restrictFlyingMobs = true;
private int togglePvPCooldown;
//TODO transform these options below into flags
private boolean allowEndermanGriefing;
private boolean endermanDeathDrop;
private boolean allowTNTDamage;
private boolean allowChestDamage;
private boolean allowCreeperDamage;
private boolean allowCreeperGriefing;
private boolean allowMobDamageToItemFrames;
@ConfigComment("Geo restrict mobs.")
@ConfigComment("Mobs that exit the island space where they were spawned will be removed.")
@ConfigEntry(path = "protection.geo-limit-settings")
private List<String> geoLimitSettings = new ArrayList<>();
// Invincible visitor settings
@ConfigComment("Invincible visitors. List of damages that will not affect visitors.")
@ -737,54 +723,12 @@ public class Settings implements DataObject, WorldSettings {
public String getWorldName() {
return worldName;
}
/**
* @return the allowChestDamage
*/
public boolean isAllowChestDamage() {
return allowChestDamage;
}
/**
* @return the allowCreeperDamage
*/
public boolean isAllowCreeperDamage() {
return allowCreeperDamage;
}
/**
* @return the allowCreeperGriefing
*/
public boolean isAllowCreeperGriefing() {
return allowCreeperGriefing;
}
/**
* @return the allowEndermanGriefing
*/
public boolean isAllowEndermanGriefing() {
return allowEndermanGriefing;
}
/**
* @return the allowMobDamageToItemFrames
*/
public boolean isAllowMobDamageToItemFrames() {
return allowMobDamageToItemFrames;
}
/**
* @return the allowObsidianScooping
*/
public boolean isAllowObsidianScooping() {
return allowObsidianScooping;
}
/**
* @return the allowPistonPush
*/
public boolean isAllowPistonPush() {
return allowPistonPush;
}
/**
* @return the allowTNTDamage
*/
public boolean isAllowTNTDamage() {
return allowTNTDamage;
}
/**
* @return the checkUpdates
*/
@ -797,12 +741,6 @@ public class Settings implements DataObject, WorldSettings {
public boolean isDeathsSumTeam() {
return deathsSumTeam;
}
/**
* @return the endermanDeathDrop
*/
public boolean isEndermanDeathDrop() {
return endermanDeathDrop;
}
/**
* @return the endGenerate
*/
@ -898,12 +836,6 @@ public class Settings implements DataObject, WorldSettings {
public boolean isRespawnOnIsland() {
return respawnOnIsland;
}
/**
* @return the restrictFlyingMobs
*/
public boolean isRestrictFlyingMobs() {
return restrictFlyingMobs;
}
/**
* @return the useEconomy
*/
@ -917,54 +849,12 @@ public class Settings implements DataObject, WorldSettings {
public boolean isUseOwnGenerator() {
return useOwnGenerator;
}
/**
* @param allowChestDamage the allowChestDamage to set
*/
public void setAllowChestDamage(boolean allowChestDamage) {
this.allowChestDamage = allowChestDamage;
}
/**
* @param allowCreeperDamage the allowCreeperDamage to set
*/
public void setAllowCreeperDamage(boolean allowCreeperDamage) {
this.allowCreeperDamage = allowCreeperDamage;
}
/**
* @param allowCreeperGriefing the allowCreeperGriefing to set
*/
public void setAllowCreeperGriefing(boolean allowCreeperGriefing) {
this.allowCreeperGriefing = allowCreeperGriefing;
}
/**
* @param allowEndermanGriefing the allowEndermanGriefing to set
*/
public void setAllowEndermanGriefing(boolean allowEndermanGriefing) {
this.allowEndermanGriefing = allowEndermanGriefing;
}
/**
* @param allowMobDamageToItemFrames the allowMobDamageToItemFrames to set
*/
public void setAllowMobDamageToItemFrames(boolean allowMobDamageToItemFrames) {
this.allowMobDamageToItemFrames = allowMobDamageToItemFrames;
}
/**
* @param allowObsidianScooping the allowObsidianScooping to set
*/
public void setAllowObsidianScooping(boolean allowObsidianScooping) {
this.allowObsidianScooping = allowObsidianScooping;
}
/**
* @param allowPistonPush the allowPistonPush to set
*/
public void setAllowPistonPush(boolean allowPistonPush) {
this.allowPistonPush = allowPistonPush;
}
/**
* @param allowTNTDamage the allowTNTDamage to set
*/
public void setAllowTNTDamage(boolean allowTNTDamage) {
this.allowTNTDamage = allowTNTDamage;
}
/**
* @param checkUpdates the checkUpdates to set
*/
@ -1037,12 +927,6 @@ public class Settings implements DataObject, WorldSettings {
public void setDefaultLanguage(String defaultLanguage) {
this.defaultLanguage = defaultLanguage;
}
/**
* @param endermanDeathDrop the endermanDeathDrop to set
*/
public void setEndermanDeathDrop(boolean endermanDeathDrop) {
this.endermanDeathDrop = endermanDeathDrop;
}
/**
* @param endGenerate the endGenerate to set
*/
@ -1253,12 +1137,6 @@ public class Settings implements DataObject, WorldSettings {
public void setRespawnOnIsland(boolean respawnOnIsland) {
this.respawnOnIsland = respawnOnIsland;
}
/**
* @param restrictFlyingMobs the restrictFlyingMobs to set
*/
public void setRestrictFlyingMobs(boolean restrictFlyingMobs) {
this.restrictFlyingMobs = restrictFlyingMobs;
}
/**
* @param seaHeight the seaHeight to set
*/
@ -1588,6 +1466,19 @@ public class Settings implements DataObject, WorldSettings {
public boolean isWaterUnsafe() {
return false;
}
/**
* @return the geoLimitSettings
*/
@Override
public List<String> getGeoLimitSettings() {
return geoLimitSettings;
}
/**
* @param geoLimitSettings the geoLimitSettings to set
*/
public void setGeoLimitSettings(List<String> geoLimitSettings) {
this.geoLimitSettings = geoLimitSettings;
}
}

View File

@ -230,4 +230,9 @@ public interface WorldSettings {
* @return true if water is not safe in this world, e.g, should not be a home location
*/
boolean isWaterUnsafe();
/**
* @return list of entity types that should not exit the island limits
*/
List<String> getGeoLimitSettings();
}

View File

@ -327,6 +327,11 @@ public class Island implements DataObject {
return x >= getMinX() && x < getMinX() + range*2 && z >= getMinZ() && z < getMinZ() + range*2;
}
/**
* Checks if location is in full island space, not just protected space
* @param location - location
* @return true if in island space
*/
public boolean inIslandSpace(Location location) {
return Util.sameWorld(world, location.getWorld()) && inIslandSpace(location.getBlockX(), location.getBlockZ());
}

View File

@ -0,0 +1,103 @@
/**
*
*/
package us.tastybento.bskyblock.listeners.flags;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.entity.EntityType;
import org.bukkit.event.inventory.ClickType;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.api.panels.Panel;
import us.tastybento.bskyblock.api.panels.PanelItem;
import us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler;
import us.tastybento.bskyblock.api.panels.builders.PanelBuilder;
import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder;
import us.tastybento.bskyblock.api.user.User;
import us.tastybento.bskyblock.managers.IslandWorldManager;
import us.tastybento.bskyblock.util.Util;
/**
* Provide geo limiting to mobs - removed them if they go outside island bounds
* @author tastybento
*
*/
public class GeoLimitClickListener implements ClickHandler {
/**
* A list of all living entity types, minus some
*/
private final List<EntityType> livingEntityTypes = Arrays.stream(EntityType.values())
.filter(EntityType::isAlive)
.filter(t -> !(t.equals(EntityType.PLAYER) || t.equals(EntityType.GIANT) || t.equals(EntityType.ARMOR_STAND)))
.sorted(Comparator.comparing(EntityType::name))
.collect(Collectors.toList());
@Override
public boolean onClick(Panel panel, User user, ClickType clickType, int slot) {
// Get the world
if (!user.inWorld()) {
user.sendMessage("general.errors.wrong-world");
return true;
}
IslandWorldManager iwm = BSkyBlock.getInstance().getIWM();
String reqPerm = iwm.getPermissionPrefix(Util.getWorld(user.getWorld())) + ".admin.settings.GEO_LIMIT_MOBS";
if (!user.hasPermission(reqPerm)) {
user.sendMessage("general.errors.no-permission");
user.sendMessage("general.errors.you-need", "[permission]", reqPerm);
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
return true;
}
String panelName = user.getTranslation("protection.flags.GEO_LIMIT_MOBS.name");
if (panel.getName().equals(panelName)) {
// This is a click on the geo limit panel
// Slot relates to the enum
EntityType c = livingEntityTypes.get(slot);
if (iwm.getGeoLimitSettings(user.getWorld()).contains(c.name())) {
iwm.getGeoLimitSettings(user.getWorld()).remove(c.name());
} else {
iwm.getGeoLimitSettings(user.getWorld()).add(c.name());
}
// Apply change to panel
panel.getInventory().setItem(slot, getPanelItem(c, user).getItem());
} else {
// Open the Sub Settings panel
openPanel(user, panelName);
}
return true;
}
private void openPanel(User user, String panelName) {
// Close the current panel
user.closeInventory();
// Open a new panel
PanelBuilder pb = new PanelBuilder();
pb.user(user).name(panelName);
// Make panel items
livingEntityTypes.forEach(c -> pb.item(getPanelItem(c, user)));
pb.build();
}
private PanelItem getPanelItem(EntityType c, User user) {
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(Util.prettifyText(c.toString()));
pib.clickHandler(this);
if (BSkyBlock.getInstance().getIWM().getGeoLimitSettings(user.getWorld()).contains(c.name())) {
pib.icon(Material.GREEN_SHULKER_BOX);
pib.description(user.getTranslation("protection.panel.flag-item.setting-active"));
} else {
pib.icon(Material.RED_SHULKER_BOX);
pib.description(user.getTranslation("protection.panel.flag-item.setting-disabled"));
}
return pib.build();
}
}

View File

@ -0,0 +1,85 @@
/**
*
*/
package us.tastybento.bskyblock.listeners.flags;
import java.util.Map;
import java.util.WeakHashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.ExplosionPrimeEvent;
import org.bukkit.projectiles.ProjectileSource;
import us.tastybento.bskyblock.api.events.BSBReadyEvent;
import us.tastybento.bskyblock.api.flags.AbstractFlagListener;
import us.tastybento.bskyblock.database.objects.Island;
/**
* Provide geo limiting to mobs - removed them if they go outside island bounds
* @author tastybento
*
*/
public class GeoLimitMobsListener extends AbstractFlagListener {
private Map<Entity, Island> mobSpawnTracker = new WeakHashMap<>();
/**
* Start the tracker when the plugin is loaded
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void pluginReady(BSBReadyEvent event) {
// Kick off the task to remove entities that go outside island boundaries
Bukkit.getScheduler().runTaskTimer(getPlugin(), () -> {
mobSpawnTracker.entrySet().stream()
.filter(e -> !e.getValue().onIsland(e.getKey().getLocation()))
.map(Map.Entry::getKey)
.forEach(Entity::remove);
mobSpawnTracker.keySet().removeIf(e -> e == null || e.isDead());
}, 20L, 20L);
}
/**
* Track where the mob was created. This will determine its allowable movement zone.
* @param e - event
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void mobSpawn(CreatureSpawnEvent e) {
if (getIWM().inWorld(e.getLocation())
&& getIWM().getGeoLimitSettings(e.getLocation().getWorld()).contains(e.getEntityType().name())) {
getIslands().getIslandAt(e.getLocation()).ifPresent(i -> mobSpawnTracker.put(e.getEntity(), i));
}
}
/**
* Clean up the map when entity dies (does not handle entity removal)
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void MobDeath(final EntityDeathEvent e) {
mobSpawnTracker.remove(e.getEntity());
}
/**
* Deal with projectiles fired by entities
* @param e
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void ProjectileExplode(final ExplosionPrimeEvent e) {
if (e.getEntity() instanceof Projectile && getIWM().inWorld(e.getEntity().getLocation())) {
ProjectileSource source = ((Projectile)e.getEntity()).getShooter();
if (source instanceof Entity) {
Entity shooter = (Entity)source;
if (mobSpawnTracker.containsKey(shooter)
&& !mobSpawnTracker.get(shooter).onIsland(e.getEntity().getLocation())) {
e.getEntity().remove();
e.setCancelled(true);
}
}
}
}
}

View File

@ -23,6 +23,7 @@ import us.tastybento.bskyblock.listeners.flags.EndermanListener;
import us.tastybento.bskyblock.listeners.flags.EnterExitListener;
import us.tastybento.bskyblock.listeners.flags.EntityInteractListener;
import us.tastybento.bskyblock.listeners.flags.FireListener;
import us.tastybento.bskyblock.listeners.flags.GeoLimitClickListener;
import us.tastybento.bskyblock.listeners.flags.GeoLimitMobsListener;
import us.tastybento.bskyblock.listeners.flags.HurtingListener;
import us.tastybento.bskyblock.listeners.flags.InventoryListener;
@ -191,9 +192,8 @@ public class Flags {
public static final Flag INVINCIBLE_VISITORS = new FlagBuilder().id("INVINCIBLE_VISITORS").icon(Material.DIAMOND_CHESTPLATE).type(Type.WORLD_SETTING)
.listener(ilv).onClick(ilv).subPanel(true).build();
private static GeoLimitMobsListener glm = new GeoLimitMobsListener();
static final Flag GEO_LIMIT_MOBS = new FlagBuilder().id("GEO_LIMIT_MOBS").icon(Material.CHAINMAIL_CHESTPLATE).type(Type.WORLD_SETTING)
.listener(glm).onClick(glm).subPanel(true).build();
public static final Flag GEO_LIMIT_MOBS = new FlagBuilder().id("GEO_LIMIT_MOBS").icon(Material.CHAINMAIL_CHESTPLATE).type(Type.WORLD_SETTING)
.listener(new GeoLimitMobsListener()).onClick(new GeoLimitClickListener()).subPanel(true).build();
public static final Flag REMOVE_MOBS = new FlagBuilder().id("REMOVE_MOBS").icon(Material.GLOWSTONE_DUST).type(Type.WORLD_SETTING)
.listener(new RemoveMobsListener()).allowedByDefault(true).build();

View File

@ -573,7 +573,7 @@ public class IslandWorldManager {
*
* @param world
* - world
* @return invisible visitor settings
* @return invincible visitor settings
*/
public List<String> getIvSettings(World world) {
return worldSettings.get(Util.getWorld(world)).getIvSettings();
@ -725,4 +725,13 @@ public class IslandWorldManager {
public boolean isWaterNotSafe(World world) {
return worldSettings.get(Util.getWorld(world)).isWaterUnsafe();
}
/**
* Get a list of entity types that should not exit the island limits
* @param world - world
* @return list
*/
public List<String> getGeoLimitSettings(World world) {
return worldSettings.get(Util.getWorld(world)).getGeoLimitSettings();
}
}