Address #3 and #4

This commit is contained in:
Simon Chuu 2019-03-29 22:37:47 -04:00
parent c6fd4b2604
commit 989b2bb07c
5 changed files with 290 additions and 165 deletions

View File

@ -0,0 +1,51 @@
package com.simonorj.mc.phantomsmp;
import org.bukkit.configuration.file.FileConfiguration;
class ConfigSaver {
static final String REMOVE_TARGETING_RESTED_NODE = "remove-targeting-rested";
static final String DISALLOW_SPAWNING_FOR_NODE = "disallow-targeting-for";
static final String CONFIG_VERSION_NODE = "config-version";
static final int version = 1;
private static final String HEADER =
"# PhantomSMP by Simon Chuu\n" +
"\n" +
"# For help, follow the plugin project link below:\n" +
"# https://github.com/SimonOrJ/PhantomSMP/\n";
private static final String REMOVE_TARGETING_RESTED =
"# Remove phantoms that try to target player slept within three (Minecraft)\n" +
"# days?\n" +
"# true = remove phantom targeting rested player\n" +
"# false = Keep phantoms (and make phantoms try to target another player)\n";
private static final String DISALLOW_SPAWNING_FOR =
"# How many ticks since player rested should phantoms ignore the player?\n" +
"# NOTE: Any value under 72000 (3 full Minecraft days) will essentially be\n" +
"# ignored for phantom spawning. It will only have an effect on already\n" +
"# spawned phantoms only.\n";
private static final String CONFIG_VERSION =
"# Keeps track of configuration version -- do not change!";
static String saveToString(FileConfiguration config) {
boolean remove = config.getBoolean(REMOVE_TARGETING_RESTED_NODE, true);
int disallow = config.getInt(DISALLOW_SPAWNING_FOR_NODE, 72000);
return HEADER +
"\n" +
REMOVE_TARGETING_RESTED +
REMOVE_TARGETING_RESTED_NODE +
": " + remove +
"\n\n" +
DISALLOW_SPAWNING_FOR +
DISALLOW_SPAWNING_FOR_NODE +
": " + disallow +
"\n\n" +
CONFIG_VERSION +
CONFIG_VERSION_NODE +
": " + version +
"\n";
}
}

View File

@ -0,0 +1,185 @@
package com.simonorj.mc.phantomsmp;
import org.bukkit.Statistic;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Phantom;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
public class PhantomListener implements Listener {
private static final String DISALLOW_SPAWN_PERM = "phantomsmp.disallowspawn";
private static final String IGNORE_PERM = "phantomsmp.ignore";
private Map<Player, LinkedHashSet<Phantom>> playerPhantomMap = new HashMap<>();
private Map<Phantom, Player> phantomPlayerMap = new HashMap<>();
private PhantomSMP plugin;
PhantomListener() {
this.plugin = PhantomSMP.getInstance();
for (Player p : plugin.getServer().getOnlinePlayers()) {
this.playerPhantomMap.put(p, new LinkedHashSet<>());
}
// Initiate map
for (World w : plugin.getServer().getWorlds()) {
if (w.getEnvironment() != World.Environment.NORMAL)
continue;
for (Entity e : w.getLivingEntities())
if (e instanceof Phantom)
addPhantom((Phantom) e);
}
}
void disable() {
this.playerPhantomMap = null;
this.phantomPlayerMap = null;
this.plugin = null;
}
private boolean phantomSpawnAllowed(Player p) {
return p.getStatistic(Statistic.TIME_SINCE_REST) > plugin.disallowSpawningFor || p.hasPermission(DISALLOW_SPAWN_PERM);
}
private boolean phantomIgnore(Player p) {
return plugin.removeTargetingRested || p.hasPermission(IGNORE_PERM);
}
private void addPhantom(Phantom phantom) {
addPhantom(phantom, null);
}
private void addPhantom(Phantom phantom, Cancellable e) {
addPhantom(phantom, null, e);
}
private void addPhantom(Phantom phantom, Player newTarget, Cancellable e) {
if (newTarget == null && !(phantom.getTarget() instanceof Player)) {
return;
}
Player p = newTarget != null ? newTarget : (Player) phantom.getTarget();
// Player rested before
if (!phantomSpawnAllowed(p)) {
if (e != null)
e.setCancelled(true);
if (phantomIgnore(p))
if (phantom.getCustomName() == null)
phantom.remove();
}
// Phantom spawn is legal
playerPhantomMap.computeIfAbsent(p, k -> new LinkedHashSet<>()).add(phantom);
phantomPlayerMap.put(phantom, p);
}
private void removePlayerPhantom(Player p) {
Iterator<Phantom> i = playerPhantomMap.get(p).iterator();
while(i.hasNext()) {
Phantom phantom = i.next();
if (phantom.getTarget() == p) {
phantomPlayerMap.remove(phantom);
phantom.setTarget(null);
}
i.remove();
}
}
// Initiate when player joins
@EventHandler(priority = EventPriority.MONITOR)
public void playerJoin(PlayerJoinEvent e) {
playerPhantomMap.put(e.getPlayer(), new LinkedHashSet<>());
}
// Reset when player leaves
@EventHandler
public void playerLeave(PlayerQuitEvent e) {
Player p = e.getPlayer();
for (Phantom phantom : playerPhantomMap.get(p)) {
if (phantom.getTarget() == p) {
phantom.setTarget(null);
phantomPlayerMap.remove(phantom);
}
}
}
// Remove phantoms when player sleeps
@EventHandler
public void playerUseBed(PlayerBedEnterEvent e) {
if (e.isCancelled())
return;
removePlayerPhantom(e.getPlayer());
}
@EventHandler
public void playerDied(PlayerDeathEvent e) {
removePlayerPhantom(e.getEntity());
}
// Check phantom when they spawn wrongly
@EventHandler
public void phantomSpawn(CreatureSpawnEvent e) {
if (e.isCancelled() || !(e.getEntity() instanceof Phantom)) {
return;
}
addPhantom((Phantom) e.getEntity(), e);
}
// Remove phantom that targets player who slept
@EventHandler
public void phantomTarget(EntityTargetLivingEntityEvent e) {
if (e.isCancelled() || !(e.getEntity() instanceof Phantom && e.getTarget() instanceof Player)) {
return;
}
addPhantom((Phantom) e.getEntity(), (Player) e.getTarget(), e);
}
// Check phantom in loaded chunks
@EventHandler
public void phantomInLoadedChunk(ChunkLoadEvent e) {
if (e.getWorld().getEnvironment() != World.Environment.NORMAL)
return;
for (Entity ent : e.getChunk().getEntities())
if (ent instanceof Phantom)
addPhantom((Phantom) ent);
}
// Check phantom on death
@EventHandler
public void phantomDied(EntityDeathEvent e) {
if (!(e.getEntity() instanceof Phantom))
return;
Phantom phantom = (Phantom) e.getEntity();
Player p = phantomPlayerMap.remove(phantom);
if (p == null)
return;
playerPhantomMap.get(p).remove(phantom);
}
}

View File

@ -1,189 +1,57 @@
package com.simonorj.mc.phantomsmp;
import org.bukkit.Statistic;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Phantom;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import com.google.common.base.Charsets;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.io.*;
import java.util.logging.Level;
public class PhantomSMP extends JavaPlugin {
private Map<Player, LinkedHashSet<Phantom>> playerPhantomMap = null;
private Map<Phantom, Player> phantomPlayerMap = null;
private boolean removeTargetingRested = true;
private static final int TIME_SINCE_REST_PHANTOM_SPAWN = 72000;
private static PhantomSMP instance = null;
private PhantomListener listener = null;
boolean removeTargetingRested;
int disallowSpawningFor;
@Override
public void onEnable() {
PhantomSMP.instance = this;
saveDefaultConfig();
this.playerPhantomMap = new HashMap<>();
this.phantomPlayerMap = new HashMap<>();
this.removeTargetingRested = getConfig().getBoolean("remove-targeting-rested", true);
if (getConfig().getInt(ConfigSaver.CONFIG_VERSION_NODE) != ConfigSaver.version)
saveConfig();
for (Player p : getServer().getOnlinePlayers()) {
this.playerPhantomMap.put(p, new LinkedHashSet<>());
}
this.removeTargetingRested = getConfig().getBoolean(ConfigSaver.REMOVE_TARGETING_RESTED_NODE, true);
this.disallowSpawningFor= getConfig().getInt(ConfigSaver.DISALLOW_SPAWNING_FOR_NODE, 72000);
// Initiate map
for (World w : getServer().getWorlds()) {
if (w.getEnvironment() != World.Environment.NORMAL)
continue;
for (Entity e : w.getLivingEntities())
if (e instanceof Phantom)
addPhantom((Phantom) e);
}
getServer().getPluginManager().registerEvents(new PhantomListener(), this);
this.listener = new PhantomListener();
getServer().getPluginManager().registerEvents(listener, this);
}
@Override
public void onDisable() {
playerPhantomMap = null;
phantomPlayerMap = null;
PhantomSMP.instance = null;
listener.disable();
listener = null;
}
private void addPhantom(Phantom phantom) {
addPhantom(phantom, null);
}
@Override
public void saveConfig() {
File configFile = new File(getDataFolder(), "config.yml");
private void addPhantom(Phantom phantom, Cancellable e) {
addPhantom(phantom, null, e);
}
try {
//noinspection ResultOfMethodCallIgnored
configFile.mkdirs();
String data = ConfigSaver.saveToString(getConfig());
private void addPhantom(Phantom phantom, Player newTarget, Cancellable e) {
if (newTarget == null && !(phantom.getTarget() instanceof Player)) {
return;
}
Player p = newTarget != null ? newTarget : (Player) phantom.getTarget();
// Player rested before
if (!phantomSpawnAllowed(p)) {
if (e != null)
e.setCancelled(true);
if (removeTargetingRested)
if (phantom.getCustomName() == null)
phantom.remove();
}
// Phantom spawn is legal
playerPhantomMap.computeIfAbsent(p, k -> new LinkedHashSet<>()).add(phantom);
phantomPlayerMap.put(phantom, p);
}
private void removePlayerPhantom(Player p) {
Iterator<Phantom> i = playerPhantomMap.get(p).iterator();
while(i.hasNext()) {
Phantom phantom = i.next();
if (phantom.getTarget() == p) {
phantomPlayerMap.remove(phantom);
phantom.setTarget(null);
try (Writer writer = new OutputStreamWriter(new FileOutputStream(configFile), Charsets.UTF_8)) {
writer.write(data);
}
i.remove();
} catch (IOException e) {
getLogger().log(Level.SEVERE, "Could not save config to " + configFile, e);
}
}
private boolean phantomSpawnAllowed(Player p) {
return p.getStatistic(Statistic.TIME_SINCE_REST) > TIME_SINCE_REST_PHANTOM_SPAWN;
}
public class PhantomListener implements Listener {
// Initiate when player joins
@EventHandler(priority = EventPriority.MONITOR)
public void playerJoin(PlayerJoinEvent e) {
playerPhantomMap.put(e.getPlayer(), new LinkedHashSet<>());
}
// Reset when player leaves
@EventHandler
public void playerLeave(PlayerQuitEvent e) {
Player p = e.getPlayer();
for (Phantom phantom : playerPhantomMap.get(p)) {
if (phantom.getTarget() == p) {
phantom.setTarget(null);
phantomPlayerMap.remove(phantom);
}
}
}
// Remove phantoms when player sleeps
@EventHandler
public void playerUseBed(PlayerBedEnterEvent e) {
if (e.isCancelled())
return;
removePlayerPhantom(e.getPlayer());
}
@EventHandler
public void playerDied(PlayerDeathEvent e) {
removePlayerPhantom(e.getEntity());
}
// Check phantom when they spawn wrongly
@EventHandler
public void phantomSpawn(CreatureSpawnEvent e) {
if (e.isCancelled() || !(e.getEntity() instanceof Phantom)) {
return;
}
addPhantom((Phantom) e.getEntity(), e);
}
// Remove phantom that targets player who slept
@EventHandler
public void phantomTarget(EntityTargetLivingEntityEvent e) {
if (e.isCancelled() || !(e.getEntity() instanceof Phantom && e.getTarget() instanceof Player)) {
return;
}
addPhantom((Phantom) e.getEntity(), (Player) e.getTarget(), e);
}
// Check phantom in loaded chunks
@EventHandler
public void phantomInLoadedChunk(ChunkLoadEvent e) {
if (e.getWorld().getEnvironment() != World.Environment.NORMAL)
return;
for (Entity ent : e.getChunk().getEntities())
if (ent instanceof Phantom)
addPhantom((Phantom) ent);
}
// Check phantom on death
@EventHandler
public void phantomDied(EntityDeathEvent e) {
if (!(e.getEntity() instanceof Phantom))
return;
Phantom phantom = (Phantom) e.getEntity();
Player p = phantomPlayerMap.remove(phantom);
if (p == null)
return;
playerPhantomMap.get(p).remove(phantom);
}
public static PhantomSMP getInstance() {
return instance;
}
}

View File

@ -1,6 +1,16 @@
# PhantomSMP by SimonOrJ
# PhantomSMP by Simon Chuu
# Remove phantoms that try to target player slept within three (Minecraft) days?
# For help, follow the plugin project link below:
# https://github.com/SimonOrJ/PhantomSMP/
# Remove phantoms that try to target player slept within three (Minecraft)
# days?
# true = remove phantom targeting rested player
# false = Keep phantoms (and make phantoms try to target another player)
remove-targeting-rested: true
# How many ticks since player rested should phantoms ignore the player?
# NOTE: Any value under 72000 (3 full Minecraft days) will essentially be
# ignored for phantom spawning. It will only have an effect on already
# spawned phantoms only.
disallow-targeting-for: 72000

View File

@ -5,3 +5,14 @@ description: Advanced phantom mechanics especially for SMP servers
main: com.simonorj.mc.phantomsmp.PhantomSMP
website: https://github.com/SimonOrJ/PhantomSMP
api-version: 1.13
permissions:
phantomsmp.disallowspawn:
description: Don't let phantom spawn on the player at all.
default: false
phantomsmp.ignore:
description: >
Don't let phantoms target the player or be removed.
This will ignore `remove-targeting-rested` configuration option for the
player and keep the phantom flying around.
default: false