Formatting changes and more consistent LookClose comparator behaviour

This commit is contained in:
fullwall 2016-01-12 00:02:07 +08:00
parent d7aefcf0db
commit 7259ea9832
4 changed files with 239 additions and 237 deletions

View File

@ -283,8 +283,9 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
getServer().getPluginManager().registerEvents(new EventListen(storedRegistries), this);
if (Setting.NPC_COST.asDouble() > 0)
if (Setting.NPC_COST.asDouble() > 0) {
setupEconomy();
}
registerCommands();
enableSubPlugins();

View File

@ -1,5 +1,9 @@
package net.citizensnpcs.npc.ai;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.ai.NavigatorParameters;
import net.citizensnpcs.api.ai.TargetType;
@ -13,10 +17,6 @@ import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.Util;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.util.Vector;
public class AStarNavigationStrategy extends AbstractPathStrategy {
private final Location destination;
private final NPC npc;

View File

@ -14,6 +14,12 @@ import java.util.WeakHashMap;
import javax.annotation.Nullable;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
@ -24,12 +30,6 @@ import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCRegistry;
import net.citizensnpcs.util.NMS;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
/**
* Tracks skin updates for players.
*
@ -39,9 +39,9 @@ public class SkinUpdateTracker {
private final Map<SkinnableEntity, Void> navigating = new WeakHashMap<SkinnableEntity, Void>(25);
private final NPCRegistry npcRegistry;
private final Map<UUID, PlayerTracker> playerTrackers = new HashMap<UUID, PlayerTracker>(
Bukkit.getMaxPlayers() / 2);
private final Map<String, NPCRegistry> registries;
private final Map<UUID, PlayerTracker> playerTrackers =
new HashMap<UUID, PlayerTracker>(Bukkit.getMaxPlayers() / 2);
private final NPCNavigationUpdater updater = new NPCNavigationUpdater();
/**
@ -63,80 +63,104 @@ public class SkinUpdateTracker {
new NPCNavigationTracker().runTaskTimer(CitizensAPI.getPlugin(), 3, 7);
}
/**
* Update a player with skin related packets from nearby skinnable NPC's.
*
* @param player
* The player to update.
* @param delay
* The delay before sending the packets.
* @param reset
* True to hard reset the players tracking info, otherwise false.
*/
public void updatePlayer(final Player player, long delay, final boolean reset) {
if (player.hasMetadata("NPC"))
return;
// determines if a player is near a skinnable entity and, if checkFov set, if the
// skinnable entity is within the players field of view.
private boolean canSee(Player player, SkinnableEntity skinnable, boolean checkFov) {
Player entity = skinnable.getBukkitEntity();
if (entity == null)
return false;
new BukkitRunnable() {
@Override
public void run() {
List<SkinnableEntity> visible = getNearbyNPCs(player, reset, false);
for (SkinnableEntity skinnable : visible) {
skinnable.getSkinTracker().updateViewer(player);
}
if (!player.canSee(entity))
return false;
if (!player.getWorld().equals(entity.getWorld()))
return false;
Location playerLoc = player.getLocation(CACHE_LOCATION);
Location skinLoc = entity.getLocation(NPC_LOCATION);
double viewDistance = Settings.Setting.NPC_SKIN_VIEW_DISTANCE.asDouble();
viewDistance *= viewDistance;
if (playerLoc.distanceSquared(skinLoc) > viewDistance)
return false;
// see if the NPC is within the players field of view
if (checkFov) {
double deltaX = skinLoc.getX() - playerLoc.getX();
double deltaZ = skinLoc.getZ() - playerLoc.getZ();
double angle = Math.atan2(deltaX, deltaZ);
float skinYaw = NMS.clampYaw(-(float) Math.toDegrees(angle));
float playerYaw = NMS.clampYaw(playerLoc.getYaw());
float upperBound = playerYaw + FIELD_OF_VIEW;
float lowerBound = playerYaw - FIELD_OF_VIEW;
return skinYaw >= lowerBound && skinYaw <= upperBound;
}
return true;
}
private Iterable<NPC> getAllNPCs() {
return Iterables.filter(Iterables.concat(npcRegistry, Iterables.concat(registries.values())),
Predicates.notNull());
}
private List<SkinnableEntity> getNearbyNPCs(Player player, boolean reset, boolean checkFov) {
List<SkinnableEntity> results = new ArrayList<SkinnableEntity>();
PlayerTracker tracker = getTracker(player, reset);
for (NPC npc : getAllNPCs()) {
SkinnableEntity skinnable = getSkinnable(npc);
if (skinnable == null)
continue;
// if checking field of view, don't add skins that have already been updated for FOV
if (checkFov && tracker.fovVisibleSkins.contains(skinnable))
continue;
if (canSee(player, skinnable, checkFov)) {
results.add(skinnable);
}
}.runTaskLater(CitizensAPI.getPlugin(), delay);
}
return results;
}
/**
* Remove a player from the tracker.
*
* <p>
* Used when the player logs out.
* </p>
*
* @param playerId
* The ID of the player.
*/
public void removePlayer(UUID playerId) {
Preconditions.checkNotNull(playerId);
playerTrackers.remove(playerId);
}
// get all navigating skinnable NPC's within the players FOV that have not been "seen" yet
private void getNewVisibleNavigating(Player player, Collection<SkinnableEntity> output) {
PlayerTracker tracker = getTracker(player, false);
/**
* Reset all players currently being tracked.
*
* <p>
* Used when Citizens is reloaded.
* </p>
*/
public void reset() {
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.hasMetadata("NPC"))
for (SkinnableEntity skinnable : navigating.keySet()) {
// make sure player hasn't already been updated to prevent excessive tab list flashing
// while NPC's are navigating and to reduce the number of times #canSee is invoked.
if (tracker.fovVisibleSkins.contains(skinnable))
continue;
PlayerTracker tracker = playerTrackers.get(player.getUniqueId());
if (tracker == null)
continue;
tracker.hardReset(player);
if (canSee(player, skinnable, true))
output.add(skinnable);
}
}
/**
* Invoke when an NPC is spawned.
*
* @param npc
* The spawned NPC.
*/
public void onNPCSpawn(NPC npc) {
Preconditions.checkNotNull(npc);
SkinnableEntity skinnable = getSkinnable(npc);
if (skinnable == null)
return;
@Nullable
private SkinnableEntity getSkinnable(NPC npc) {
Entity entity = npc.getEntity();
if (entity == null)
return null;
// reset nearby players in case they are not looking at the NPC when it spawns.
resetNearbyPlayers(skinnable);
return NMS.getSkinnable(entity);
}
// get a players tracker, create new one if not exists.
private PlayerTracker getTracker(Player player, boolean reset) {
PlayerTracker tracker = playerTrackers.get(player.getUniqueId());
if (tracker == null) {
tracker = new PlayerTracker(player);
playerTrackers.put(player.getUniqueId(), tracker);
} else if (reset) {
tracker.hardReset(player);
}
return tracker;
}
/**
@ -188,6 +212,22 @@ public class SkinUpdateTracker {
navigating.remove(skinnable);
}
/**
* Invoke when an NPC is spawned.
*
* @param npc
* The spawned NPC.
*/
public void onNPCSpawn(NPC npc) {
Preconditions.checkNotNull(npc);
SkinnableEntity skinnable = getSkinnable(npc);
if (skinnable == null)
return;
// reset nearby players in case they are not looking at the NPC when it spawns.
resetNearbyPlayers(skinnable);
}
/**
* Invoke when a player moves.
*
@ -206,6 +246,41 @@ public class SkinUpdateTracker {
updatePlayer(player, 10, false);
}
/**
* Remove a player from the tracker.
*
* <p>
* Used when the player logs out.
* </p>
*
* @param playerId
* The ID of the player.
*/
public void removePlayer(UUID playerId) {
Preconditions.checkNotNull(playerId);
playerTrackers.remove(playerId);
}
/**
* Reset all players currently being tracked.
*
* <p>
* Used when Citizens is reloaded.
* </p>
*/
public void reset() {
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.hasMetadata("NPC"))
continue;
PlayerTracker tracker = playerTrackers.get(player.getUniqueId());
if (tracker == null)
continue;
tracker.hardReset(player);
}
}
// hard reset players near a skinnable NPC
private void resetNearbyPlayers(SkinnableEntity skinnable) {
Entity entity = skinnable.getBukkitEntity();
@ -233,121 +308,105 @@ public class SkinUpdateTracker {
}
}
private List<SkinnableEntity> getNearbyNPCs(Player player, boolean reset, boolean checkFov) {
List<SkinnableEntity> results = new ArrayList<SkinnableEntity>();
PlayerTracker tracker = getTracker(player, reset);
for (NPC npc : getAllNPCs()) {
/**
* Update a player with skin related packets from nearby skinnable NPC's.
*
* @param player
* The player to update.
* @param delay
* The delay before sending the packets.
* @param reset
* True to hard reset the players tracking info, otherwise false.
*/
public void updatePlayer(final Player player, long delay, final boolean reset) {
if (player.hasMetadata("NPC"))
return;
SkinnableEntity skinnable = getSkinnable(npc);
if (skinnable == null)
continue;
new BukkitRunnable() {
@Override
public void run() {
List<SkinnableEntity> visible = getNearbyNPCs(player, reset, false);
for (SkinnableEntity skinnable : visible) {
skinnable.getSkinTracker().updateViewer(player);
}
}
}.runTaskLater(CitizensAPI.getPlugin(), delay);
}
// if checking field of view, don't add skins that have already been updated for FOV
if (checkFov && tracker.fovVisibleSkins.contains(skinnable))
continue;
// update players when the NPC navigates into their field of view
private class NPCNavigationTracker extends BukkitRunnable {
@Override
public void run() {
if (navigating.isEmpty() || playerTrackers.isEmpty())
return;
if (canSee(player, skinnable, checkFov)) {
results.add(skinnable);
List<SkinnableEntity> nearby = new ArrayList<SkinnableEntity>(10);
Collection<? extends Player> players = Bukkit.getOnlinePlayers();
for (Player player : players) {
if (player.hasMetadata("NPC"))
continue;
getNewVisibleNavigating(player, nearby);
for (SkinnableEntity skinnable : nearby) {
PlayerTracker tracker = getTracker(player, false);
tracker.fovVisibleSkins.add(skinnable);
updater.queue.offer(new UpdateInfo(player, skinnable));
}
nearby.clear();
}
}
return results;
}
private Iterable<NPC> getAllNPCs() {
return Iterables.filter(Iterables.concat(npcRegistry, Iterables.concat(registries.values())),
Predicates.notNull());
}
// Updates players. Repeating task used to schedule updates without
// causing excessive scheduling.
private class NPCNavigationUpdater extends BukkitRunnable {
Queue<UpdateInfo> queue = new ArrayDeque<UpdateInfo>(20);
// get all navigating skinnable NPC's within the players FOV that have not been "seen" yet
private void getNewVisibleNavigating(Player player, Collection<SkinnableEntity> output) {
PlayerTracker tracker = getTracker(player, false);
for (SkinnableEntity skinnable : navigating.keySet()) {
// make sure player hasn't already been updated to prevent excessive tab list flashing
// while NPC's are navigating and to reduce the number of times #canSee is invoked.
if (tracker.fovVisibleSkins.contains(skinnable))
continue;
if (canSee(player, skinnable, true))
output.add(skinnable);
@Override
public void run() {
while (!queue.isEmpty()) {
UpdateInfo info = queue.remove();
info.entity.getSkinTracker().updateViewer(info.player);
}
}
}
@Nullable
private SkinnableEntity getSkinnable(NPC npc) {
Entity entity = npc.getEntity();
if (entity == null)
return null;
return NMS.getSkinnable(entity);
}
// get a players tracker, create new one if not exists.
private PlayerTracker getTracker(Player player, boolean reset) {
PlayerTracker tracker = playerTrackers.get(player.getUniqueId());
if (tracker == null) {
tracker = new PlayerTracker(player);
playerTrackers.put(player.getUniqueId(), tracker);
}
else if (reset) {
tracker.hardReset(player);
}
return tracker;
}
// determines if a player is near a skinnable entity and, if checkFov set, if the
// skinnable entity is within the players field of view.
private boolean canSee(Player player, SkinnableEntity skinnable, boolean checkFov) {
Player entity = skinnable.getBukkitEntity();
if (entity == null)
return false;
if (!player.canSee(entity))
return false;
if (!player.getWorld().equals(entity.getWorld()))
return false;
Location playerLoc = player.getLocation(CACHE_LOCATION);
Location skinLoc = entity.getLocation(NPC_LOCATION);
double viewDistance = Settings.Setting.NPC_SKIN_VIEW_DISTANCE.asDouble();
viewDistance *= viewDistance;
if (playerLoc.distanceSquared(skinLoc) > viewDistance)
return false;
// see if the NPC is within the players field of view
if (checkFov) {
double deltaX = skinLoc.getX() - playerLoc.getX();
double deltaZ = skinLoc.getZ() - playerLoc.getZ();
double angle = Math.atan2(deltaX, deltaZ);
float skinYaw = NMS.clampYaw(-(float) Math.toDegrees(angle));
float playerYaw = NMS.clampYaw(playerLoc.getYaw());
float upperBound = playerYaw + FIELD_OF_VIEW;
float lowerBound = playerYaw - FIELD_OF_VIEW;
return skinYaw >= lowerBound && skinYaw <= upperBound;
}
return true;
}
// Tracks player location and yaw to determine when the player should be updated
// with nearby skins.
private class PlayerTracker {
final Location location = new Location(null, 0, 0, 0);
final Set<SkinnableEntity> fovVisibleSkins = new HashSet<SkinnableEntity>(20);
int rotationCount;
boolean hasMoved;
float upperBound;
final Location location = new Location(null, 0, 0, 0);
float lowerBound;
int rotationCount;
float upperBound;
PlayerTracker(Player player) {
hardReset(player);
}
// reset all
void hardReset(Player player) {
this.hasMoved = false;
this.rotationCount = 0;
this.fovVisibleSkins.clear();
reset(player);
}
// resets initial yaw and location to the players current location and yaw.
void reset(Player player) {
player.getLocation(this.location);
if (rotationCount < 3) {
float rotationDegrees = Settings.Setting.NPC_SKIN_ROTATION_UPDATE_DEGREES.asFloat();
float yaw = NMS.clampYaw(this.location.getYaw());
this.upperBound = yaw + rotationDegrees;
this.lowerBound = yaw - rotationDegrees;
}
}
boolean shouldUpdate(Player player) {
Location currentLoc = player.getLocation(CACHE_LOCATION);
@ -380,75 +439,16 @@ public class SkinUpdateTracker {
if (distance > MOVEMENT_SKIN_UPDATE_DISTANCE) {
reset(player);
return true;
}
else {
} else {
return false;
}
}
// resets initial yaw and location to the players current location and yaw.
void reset(Player player) {
player.getLocation(this.location);
if (rotationCount < 3) {
float rotationDegrees = Settings.Setting.NPC_SKIN_ROTATION_UPDATE_DEGREES.asFloat();
float yaw = NMS.clampYaw(this.location.getYaw());
this.upperBound = yaw + rotationDegrees;
this.lowerBound = yaw - rotationDegrees;
}
}
// reset all
void hardReset(Player player) {
this.hasMoved = false;
this.rotationCount = 0;
this.fovVisibleSkins.clear();
reset(player);
}
}
// update players when the NPC navigates into their field of view
private class NPCNavigationTracker extends BukkitRunnable {
@Override
public void run() {
if (navigating.isEmpty() || playerTrackers.isEmpty())
return;
List<SkinnableEntity> nearby = new ArrayList<SkinnableEntity>(10);
Collection<? extends Player> players = Bukkit.getOnlinePlayers();
for (Player player : players) {
if (player.hasMetadata("NPC"))
continue;
getNewVisibleNavigating(player, nearby);
for (SkinnableEntity skinnable : nearby) {
PlayerTracker tracker = getTracker(player, false);
tracker.fovVisibleSkins.add(skinnable);
updater.queue.offer(new UpdateInfo(player, skinnable));
}
nearby.clear();
}
}
}
// Updates players. Repeating task used to schedule updates without
// causing excessive scheduling.
private class NPCNavigationUpdater extends BukkitRunnable {
Queue<UpdateInfo> queue = new ArrayDeque<UpdateInfo>(20);
@Override
public void run() {
while (!queue.isEmpty()) {
UpdateInfo info = queue.remove();
info.entity.getSkinTracker().updateViewer(info.player);
}
}
}
private static class UpdateInfo {
Player player;
SkinnableEntity entity;
Player player;
UpdateInfo(Player player, SkinnableEntity entity) {
this.player = player;
this.entity = entity;
@ -456,7 +456,7 @@ public class SkinUpdateTracker {
}
private static final Location CACHE_LOCATION = new Location(null, 0, 0, 0);
private static final Location NPC_LOCATION = new Location(null, 0, 0, 0);
private static final int MOVEMENT_SKIN_UPDATE_DISTANCE = 50 * 50;
private static final float FIELD_OF_VIEW = 70f;
private static final int MOVEMENT_SKIN_UPDATE_DISTANCE = 50 * 50;
private static final Location NPC_LOCATION = new Location(null, 0, 0, 0);
}

View File

@ -49,12 +49,13 @@ public class LookClose extends Trait implements Toggleable, CommandConfigurable
Collections.sort(nearby, new Comparator<Entity>() {
@Override
public int compare(Entity o1, Entity o2) {
if (npcLocation.getWorld() != o1.getLocation().getWorld()
|| npcLocation.getWorld() != o2.getLocation().getWorld()) {
Location l1 = o1.getLocation();
Location l2 = o2.getLocation();
if (!npcLocation.getWorld().equals(l1.getWorld()) || !npcLocation.getWorld().equals(l2.getWorld())) {
return -1;
}
double d1 = o1.getLocation().distanceSquared(npcLocation);
double d2 = o2.getLocation().distanceSquared(npcLocation);
double d1 = l1.distanceSquared(npcLocation);
double d2 = l2.distanceSquared(npcLocation);
return Double.compare(d1, d2);
}
});