Add /npc hologram viewrange, add /npc playerfilter --applywithin, work towards auto-hologram-sneak

This commit is contained in:
fullwall 2023-11-05 20:58:37 +08:00
parent de885501cd
commit 07fb13c284
1454 changed files with 964 additions and 14929 deletions

View File

@ -102,7 +102,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
@Override @Override
public OfflinePlayer getPlayer(BlockCommandSender sender) { public OfflinePlayer getPlayer(BlockCommandSender sender) {
Entity entity = NMS.getSource(sender); Entity entity = NMS.getSource(sender);
return entity != null && entity instanceof OfflinePlayer ? (OfflinePlayer) entity : null; return entity instanceof OfflinePlayer ? (OfflinePlayer) entity : null;
} }
@Override @Override
@ -182,11 +182,9 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
if (type.equalsIgnoreCase("nbt")) { if (type.equalsIgnoreCase("nbt")) {
saves = new NBTStorage(new File(folder, Setting.STORAGE_FILE.asString()), "Citizens NPC Storage"); saves = new NBTStorage(new File(folder, Setting.STORAGE_FILE.asString()), "Citizens NPC Storage");
} }
if (saves == null) { if (saves == null) {
saves = new YamlStorage(new File(folder, Setting.STORAGE_FILE.asString()), "Citizens NPC Storage"); saves = new YamlStorage(new File(folder, Setting.STORAGE_FILE.asString()), "Citizens NPC Storage");
} }
if (!saves.load()) if (!saves.load())
return null; return null;
@ -195,8 +193,9 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
private void despawnNPCs(boolean save) { private void despawnNPCs(boolean save) {
for (NPCRegistry registry : Iterables.concat(Arrays.asList(npcRegistry), citizensBackedRegistries)) { for (NPCRegistry registry : Iterables.concat(Arrays.asList(npcRegistry), citizensBackedRegistries)) {
if (registry == null) if (registry == null) {
continue; continue;
}
if (save) { if (save) {
if (registry == npcRegistry) { if (registry == npcRegistry) {
storeNPCs(false); storeNPCs(false);
@ -237,33 +236,27 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
@Override @Override
public Iterable<NPCRegistry> getNPCRegistries() { public Iterable<NPCRegistry> getNPCRegistries() {
return new Iterable<NPCRegistry>() { return () -> new Iterator<NPCRegistry>() {
Iterator<NPCRegistry> stored;
@Override @Override
public Iterator<NPCRegistry> iterator() { public boolean hasNext() {
return new Iterator<NPCRegistry>() { return stored == null ? true : stored.hasNext();
Iterator<NPCRegistry> stored; }
@Override @Override
public boolean hasNext() { public NPCRegistry next() {
return stored == null ? true : stored.hasNext(); if (stored == null) {
} stored = Iterables.concat(storedRegistries.values(), anonymousRegistries, citizensBackedRegistries)
.iterator();
return npcRegistry;
}
return stored.next();
}
@Override @Override
public NPCRegistry next() { public void remove() {
if (stored == null) { throw new UnsupportedOperationException();
stored = Iterables
.concat(storedRegistries.values(), anonymousRegistries, citizensBackedRegistries)
.iterator();
return npcRegistry;
}
return stored.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
} }
}; };
} }
@ -392,7 +385,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
registerScriptHelpers(); registerScriptHelpers();
saves = createStorage(getDataFolder()); saves = createStorage(getDataFolder());
@ -402,15 +394,12 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
locationLookup = new LocationLookup(); locationLookup = new LocationLookup();
locationLookup.runTaskTimer(CitizensAPI.getPlugin(), 0, 5); locationLookup.runTaskTimer(CitizensAPI.getPlugin(), 0, 5);
npcRegistry = new CitizensNPCRegistry(saves, "citizens"); npcRegistry = new CitizensNPCRegistry(saves, "citizens");
traitFactory = new CitizensTraitFactory(this); traitFactory = new CitizensTraitFactory(this);
traitFactory.registerTrait(TraitInfo.create(ShopTrait.class).withSupplier(() -> { traitFactory.registerTrait(TraitInfo.create(ShopTrait.class).withSupplier(() -> new ShopTrait(shops)));
return new ShopTrait(shops);
}));
selector = new NPCSelector(this); selector = new NPCSelector(this);
Bukkit.getPluginManager().registerEvents(new EventListen(), this); Bukkit.getPluginManager().registerEvents(new EventListen(), this);
@ -426,7 +415,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
if (papi != null && papi.isEnabled()) { if (papi != null && papi.isEnabled()) {
new CitizensPlaceholders(selector).register(); new CitizensPlaceholders(selector).register();
} }
setupEconomy(); setupEconomy();
registerCommands(); registerCommands();
@ -506,12 +494,11 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
@Override @Override
public void setDefaultNPCDataStore(NPCDataStore store) { public void setDefaultNPCDataStore(NPCDataStore store) {
if (store == null) { if (store == null)
throw new IllegalArgumentException("must be non-null"); throw new IllegalArgumentException("must be non-null");
}
despawnNPCs(true); despawnNPCs(true);
this.saves = store; saves = store;
this.npcRegistry = new CitizensNPCRegistry(saves, "citizens-global-" + UUID.randomUUID().toString()); npcRegistry = new CitizensNPCRegistry(saves, "citizens-global-" + UUID.randomUUID().toString());
saves.loadInto(npcRegistry); saves.loadInto(npcRegistry);
} }
@ -614,7 +601,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
} }
} }
} }
saves.loadInto(npcRegistry); saves.loadInto(npcRegistry);
shops.load(); shops.load();

View File

@ -55,7 +55,6 @@ public class CitizensPlaceholders extends PlaceholderExpansion {
b.getEntity().getLocation().distanceSquared(location))); b.getEntity().getLocation().distanceSquared(location)));
return closestNPC.isPresent() ? Integer.toString(closestNPC.get().getId()) : ""; return closestNPC.isPresent() ? Integer.toString(closestNPC.get().getId()) : "";
} }
return null; return null;
} }

View File

@ -120,11 +120,11 @@ import net.citizensnpcs.util.Util;
public class EventListen implements Listener { public class EventListen implements Listener {
private Listener chunkEventListener; private Listener chunkEventListener;
private final SkinUpdateTracker skinUpdateTracker; private SkinUpdateTracker skinUpdateTracker;
private final ListMultimap<ChunkCoord, NPC> toRespawn = ArrayListMultimap.create(64, 4); private final ListMultimap<ChunkCoord, NPC> toRespawn = ArrayListMultimap.create(64, 4);
EventListen() { EventListen() {
this.skinUpdateTracker = new SkinUpdateTracker(); skinUpdateTracker = new SkinUpdateTracker();
try { try {
Class.forName("org.bukkit.event.world.EntitiesLoadEvent"); Class.forName("org.bukkit.event.world.EntitiesLoadEvent");
Bukkit.getPluginManager().registerEvents(new Listener() { Bukkit.getPluginManager().registerEvents(new Listener() {
@ -140,7 +140,6 @@ public class EventListen implements Listener {
}, CitizensAPI.getPlugin()); }, CitizensAPI.getPlugin());
} catch (Throwable ex) { } catch (Throwable ex) {
} }
try { try {
Class.forName("org.bukkit.event.entity.EntityTransformEvent"); Class.forName("org.bukkit.event.entity.EntityTransformEvent");
Bukkit.getPluginManager().registerEvents(new Listener() { Bukkit.getPluginManager().registerEvents(new Listener() {
@ -156,23 +155,19 @@ public class EventListen implements Listener {
}, CitizensAPI.getPlugin()); }, CitizensAPI.getPlugin());
} catch (Throwable ex) { } catch (Throwable ex) {
} }
Class<?> kbc = null; Class<?> kbc = null;
try { try {
kbc = Class.forName("com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent"); kbc = Class.forName("com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
} }
if (kbc != null) { if (kbc != null) {
registerKnockbackEvent(kbc); registerKnockbackEvent(kbc);
} }
Class<?> pbeac = null; Class<?> pbeac = null;
try { try {
pbeac = Class.forName("io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent"); pbeac = Class.forName("io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
} }
if (pbeac != null) { if (pbeac != null) {
registerPushEvent(pbeac); registerPushEvent(pbeac);
} }
@ -184,8 +179,9 @@ public class EventListen implements Listener {
int limit = Setting.DEFAULT_NPC_LIMIT.asInt(); int limit = Setting.DEFAULT_NPC_LIMIT.asInt();
int maxChecks = Setting.MAX_NPC_LIMIT_CHECKS.asInt(); int maxChecks = Setting.MAX_NPC_LIMIT_CHECKS.asInt();
for (int i = maxChecks; i >= 0; i--) { for (int i = maxChecks; i >= 0; i--) {
if (!event.getCreator().hasPermission("citizens.npc.limit." + i)) if (!event.getCreator().hasPermission("citizens.npc.limit." + i)) {
continue; continue;
}
limit = i; limit = i;
break; break;
} }
@ -215,7 +211,6 @@ public class EventListen implements Listener {
if (Messaging.isDebugging() && Setting.DEBUG_CHUNK_LOADS.asBoolean() && toRespawn.containsKey(coord)) { if (Messaging.isDebugging() && Setting.DEBUG_CHUNK_LOADS.asBoolean() && toRespawn.containsKey(coord)) {
new Exception("CITIZENS CHUNK LOAD DEBUG " + coord).printStackTrace(); new Exception("CITIZENS CHUNK LOAD DEBUG " + coord).printStackTrace();
} }
if (event instanceof Cancellable) { if (event instanceof Cancellable) {
runnable.run(); runnable.run();
} else { } else {
@ -231,7 +226,7 @@ public class EventListen implements Listener {
} }
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onChunkUnload(final ChunkUnloadEvent event) { public void onChunkUnload(ChunkUnloadEvent event) {
if (chunkEventListener != null) if (chunkEventListener != null)
return; return;
@ -290,7 +285,6 @@ public class EventListen implements Listener {
} }
return; return;
} }
event.setCancelled(npc.isProtected()); event.setCancelled(npc.isProtected());
if (event instanceof EntityDamageByEntityEvent) { if (event instanceof EntityDamageByEntityEvent) {
@ -306,7 +300,6 @@ public class EventListen implements Listener {
if (npc == null) if (npc == null)
return; return;
} }
NPCLeftClickEvent leftClickEvent = new NPCLeftClickEvent(npc, damager); NPCLeftClickEvent leftClickEvent = new NPCLeftClickEvent(npc, damager);
Bukkit.getPluginManager().callEvent(leftClickEvent); Bukkit.getPluginManager().callEvent(leftClickEvent);
if (npc.hasTrait(CommandTrait.class)) { if (npc.hasTrait(CommandTrait.class)) {
@ -321,15 +314,14 @@ public class EventListen implements Listener {
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onEntityDeath(EntityDeathEvent event) { public void onEntityDeath(EntityDeathEvent event) {
final NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getEntity()); NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getEntity());
if (npc == null) if (npc == null)
return; return;
if (!npc.data().get(NPC.Metadata.DROPS_ITEMS, false)) { if (!npc.data().get(NPC.Metadata.DROPS_ITEMS, false)) {
event.getDrops().clear(); event.getDrops().clear();
} }
Location location = npc.getStoredLocation();
final Location location = npc.getStoredLocation();
Bukkit.getPluginManager().callEvent(new NPCDeathEvent(npc, event)); Bukkit.getPluginManager().callEvent(new NPCDeathEvent(npc, event));
npc.despawn(DespawnReason.DEATH); npc.despawn(DespawnReason.DEATH);
@ -438,7 +430,6 @@ public class EventListen implements Listener {
if (npc.isSpawned() && npc.getEntity().getType() == EntityType.PLAYER) { if (npc.isSpawned() && npc.getEntity().getType() == EntityType.PLAYER) {
onNPCPlayerLinkToPlayer(event); onNPCPlayerLinkToPlayer(event);
} }
ClickRedirectTrait crt = npc.getTraitNullable(ClickRedirectTrait.class); ClickRedirectTrait crt = npc.getTraitNullable(ClickRedirectTrait.class);
if (crt != null) { if (crt != null) {
HologramTrait ht = crt.getRedirectNPC().getTraitNullable(HologramTrait.class); HologramTrait ht = crt.getRedirectNPC().getTraitNullable(HologramTrait.class);
@ -468,7 +459,6 @@ public class EventListen implements Listener {
} }
return; return;
} }
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> {
if (!tracker.isValid() || !event.getPlayer().isValid()) if (!tracker.isValid() || !event.getPlayer().isValid())
return; return;
@ -488,12 +478,15 @@ public class EventListen implements Listener {
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onNPCSeenByPlayer(NPCSeenByPlayerEvent event) { public void onNPCSeenByPlayer(NPCSeenByPlayerEvent event) {
NPC npc = event.getNPC(); NPC npc = event.getNPC();
PlayerFilter pf = npc.getTraitNullable(PlayerFilter.class);
if (pf != null) {
event.setCancelled(pf.onSeenByPlayer(event.getPlayer()));
}
ClickRedirectTrait crt = npc.getTraitNullable(ClickRedirectTrait.class); ClickRedirectTrait crt = npc.getTraitNullable(ClickRedirectTrait.class);
if (crt != null) { if (crt != null) {
npc = crt.getRedirectNPC(); npc = crt.getRedirectNPC();
} }
pf = npc.getTraitNullable(PlayerFilter.class);
PlayerFilter pf = npc.getTraitNullable(PlayerFilter.class);
if (pf != null) { if (pf != null) {
event.setCancelled(pf.onSeenByPlayer(event.getPlayer())); event.setCancelled(pf.onSeenByPlayer(event.getPlayer()));
} }
@ -561,27 +554,22 @@ public class EventListen implements Listener {
if (event.getPlayer().getItemInHand().getType() == Material.NAME_TAG) { if (event.getPlayer().getItemInHand().getType() == Material.NAME_TAG) {
rightClickEvent.setCancelled(npc.isProtected()); rightClickEvent.setCancelled(npc.isProtected());
} }
Bukkit.getPluginManager().callEvent(rightClickEvent); Bukkit.getPluginManager().callEvent(rightClickEvent);
if (rightClickEvent.isCancelled()) { if (rightClickEvent.isCancelled()) {
event.setCancelled(true); event.setCancelled(true);
return; return;
} }
if (npc.hasTrait(CommandTrait.class)) { if (npc.hasTrait(CommandTrait.class)) {
npc.getTraitNullable(CommandTrait.class).dispatch(player, CommandTrait.Hand.RIGHT); npc.getTraitNullable(CommandTrait.class).dispatch(player, CommandTrait.Hand.RIGHT);
rightClickEvent.setDelayedCancellation(true); rightClickEvent.setDelayedCancellation(true);
} }
if (npc.hasTrait(ShopTrait.class)) { if (npc.hasTrait(ShopTrait.class)) {
npc.getTraitNullable(ShopTrait.class).onRightClick(player); npc.getTraitNullable(ShopTrait.class).onRightClick(player);
rightClickEvent.setDelayedCancellation(true); rightClickEvent.setDelayedCancellation(true);
} }
if (rightClickEvent.isDelayedCancellation()) { if (rightClickEvent.isDelayedCancellation()) {
event.setCancelled(true); event.setCancelled(true);
} }
if (event.isCancelled()) { if (event.isCancelled()) {
if (SUPPORT_STOP_USE_ITEM) { if (SUPPORT_STOP_USE_ITEM) {
try { try {
@ -617,7 +605,7 @@ public class EventListen implements Listener {
// recalculate player NPCs the first time a player moves and every time // recalculate player NPCs the first time a player moves and every time
// a player moves a certain distance from their last position. // a player moves a certain distance from their last position.
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(final PlayerMoveEvent event) { public void onPlayerMove(PlayerMoveEvent event) {
skinUpdateTracker.onPlayerMove(event.getPlayer()); skinUpdateTracker.onPlayerMove(event.getPlayer());
} }
@ -630,7 +618,6 @@ public class EventListen implements Listener {
event.getPlayer().leaveVehicle(); event.getPlayer().leaveVehicle();
} }
} }
skinUpdateTracker.removePlayer(event.getPlayer().getUniqueId()); skinUpdateTracker.removePlayer(event.getPlayer().getUniqueId());
CitizensAPI.getLocationLookup().onQuit(event); CitizensAPI.getLocationLookup().onQuit(event);
} }
@ -641,7 +628,7 @@ public class EventListen implements Listener {
} }
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onPlayerTeleport(final PlayerTeleportEvent event) { public void onPlayerTeleport(PlayerTeleportEvent event) {
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getPlayer()); NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getPlayer());
if (event.getCause() == TeleportCause.PLUGIN && npc != null && !npc.data().has("citizens-force-teleporting") if (event.getCause() == TeleportCause.PLUGIN && npc != null && !npc.data().has("citizens-force-teleporting")
&& Setting.PLAYER_TELEPORT_DELAY.asTicks() > 0) { && Setting.PLAYER_TELEPORT_DELAY.asTicks() > 0) {
@ -652,7 +639,6 @@ public class EventListen implements Listener {
npc.data().remove("citizens-force-teleporting"); npc.data().remove("citizens-force-teleporting");
}, Setting.PLAYER_TELEPORT_DELAY.asTicks()); }, Setting.PLAYER_TELEPORT_DELAY.asTicks());
} }
skinUpdateTracker.updatePlayer(event.getPlayer(), 15, true); skinUpdateTracker.updatePlayer(event.getPlayer(), 15, true);
} }
@ -673,9 +659,9 @@ public class EventListen implements Listener {
public void onPotionSplashEvent(PotionSplashEvent event) { public void onPotionSplashEvent(PotionSplashEvent event) {
for (LivingEntity entity : event.getAffectedEntities()) { for (LivingEntity entity : event.getAffectedEntities()) {
NPC npc = CitizensAPI.getNPCRegistry().getNPC(entity); NPC npc = CitizensAPI.getNPCRegistry().getNPC(entity);
if (npc == null) if (npc == null) {
continue; continue;
}
if (npc.isProtected()) { if (npc.isProtected()) {
event.setIntensity(entity, 0); event.setIntensity(entity, 0);
} }
@ -683,7 +669,7 @@ public class EventListen implements Listener {
} }
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onProjectileHit(final ProjectileHitEvent event) { public void onProjectileHit(ProjectileHitEvent event) {
if (!(event.getEntity() instanceof FishHook)) if (!(event.getEntity() instanceof FishHook))
return; return;
NMS.removeHookIfNecessary((FishHook) event.getEntity()); NMS.removeHookIfNecessary((FishHook) event.getEntity());
@ -696,7 +682,6 @@ public class EventListen implements Listener {
cancel(); cancel();
return; return;
} }
NMS.removeHookIfNecessary((FishHook) event.getEntity()); NMS.removeHookIfNecessary((FishHook) event.getEntity());
} }
}.runTaskTimer(CitizensAPI.getPlugin(), 0, 1); }.runTaskTimer(CitizensAPI.getPlugin(), 0, 1);
@ -735,7 +720,7 @@ public class EventListen implements Listener {
} }
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onVehicleEnter(final VehicleEnterEvent event) { public void onVehicleEnter(VehicleEnterEvent event) {
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getVehicle()); NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getVehicle());
NPC rider = CitizensAPI.getNPCRegistry().getNPC(event.getEntered()); NPC rider = CitizensAPI.getNPCRegistry().getNPC(event.getEntered());
if (npc == null) { if (npc == null) {
@ -743,10 +728,8 @@ public class EventListen implements Listener {
|| event.getVehicle() instanceof Minecart)) { || event.getVehicle() instanceof Minecart)) {
event.setCancelled(true); event.setCancelled(true);
} }
return; return;
} }
if (rider != null || !(npc instanceof Vehicle)) if (rider != null || !(npc instanceof Vehicle))
return; return;
@ -758,8 +741,10 @@ public class EventListen implements Listener {
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onWorldLoad(WorldLoadEvent event) { public void onWorldLoad(WorldLoadEvent event) {
for (ChunkCoord chunk : toRespawn.keySet()) { for (ChunkCoord chunk : toRespawn.keySet()) {
if (!chunk.worldUUID.equals(event.getWorld().getUID()) || !event.getWorld().isChunkLoaded(chunk.x, chunk.z)) if (!chunk.worldUUID.equals(event.getWorld().getUID())
|| !event.getWorld().isChunkLoaded(chunk.x, chunk.z)) {
continue; continue;
}
respawnAllFromCoord(chunk, event); respawnAllFromCoord(chunk, event);
} }
} }
@ -767,9 +752,9 @@ public class EventListen implements Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) { public void onWorldUnload(WorldUnloadEvent event) {
for (NPC npc : getAllNPCs()) { for (NPC npc : getAllNPCs()) {
if (npc == null || !npc.isSpawned() || !npc.getEntity().getWorld().equals(event.getWorld())) if (npc == null || !npc.isSpawned() || !npc.getEntity().getWorld().equals(event.getWorld())) {
continue; continue;
}
boolean despawned = npc.despawn(DespawnReason.WORLD_UNLOAD); boolean despawned = npc.despawn(DespawnReason.WORLD_UNLOAD);
if (event.isCancelled() || !despawned) { if (event.isCancelled() || !despawned) {
for (ChunkCoord coord : toRespawn.keySet()) { for (ChunkCoord coord : toRespawn.keySet()) {
@ -780,7 +765,6 @@ public class EventListen implements Listener {
event.setCancelled(true); event.setCancelled(true);
return; return;
} }
if (npc.isSpawned()) { if (npc.isSpawned()) {
toRespawn.put(new ChunkCoord(npc.getEntity().getLocation()), npc); toRespawn.put(new ChunkCoord(npc.getEntity().getLocation()), npc);
Messaging.debug("Despawned", npc, "due to world unload at", event.getWorld().getName()); Messaging.debug("Despawned", npc, "due to world unload at", event.getWorld().getName());
@ -829,9 +813,7 @@ public class EventListen implements Listener {
Method getAcceleration = clazz.getMethod("getAcceleration"); Method getAcceleration = clazz.getMethod("getAcceleration");
handlers.register(new RegisteredListener(new Listener() { handlers.register(new RegisteredListener(new Listener() {
}, (listener, event) -> { }, (listener, event) -> {
if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0 || event.getClass() != clazz)
return;
if (event.getClass() != clazz)
return; return;
try { try {
Entity entity = (Entity) getEntity.invoke(event); Entity entity = (Entity) getEntity.invoke(event);
@ -858,26 +840,22 @@ public class EventListen implements Listener {
if (ids.size() > 0) { if (ids.size() > 0) {
Messaging.debug("Respawning all NPCs at", coord, "due to", event); Messaging.debug("Respawning all NPCs at", coord, "due to", event);
} }
for (int i = 0; i < ids.size(); i++) { for (int i = 0; i < ids.size(); i++) {
NPC npc = ids.get(i); NPC npc = ids.get(i);
if (npc.getOwningRegistry().getById(npc.getId()) != npc) { if (npc.getOwningRegistry().getById(npc.getId()) != npc) {
Messaging.idebug(() -> "Prevented deregistered NPC from respawning " + npc); Messaging.idebug(() -> "Prevented deregistered NPC from respawning " + npc);
continue; continue;
} }
if (npc.isSpawned()) { if (npc.isSpawned()) {
Messaging.idebug(() -> "Can't respawn NPC " + npc + ": already spawned"); Messaging.idebug(() -> "Can't respawn NPC " + npc + ": already spawned");
continue; continue;
} }
boolean success = spawn(npc); boolean success = spawn(npc);
if (!success) { if (!success) {
ids.remove(i--); ids.remove(i--);
Messaging.idebug(() -> Joiner.on(' ').join("Couldn't respawn", npc, "during", event, "at", coord)); Messaging.idebug(() -> Joiner.on(' ').join("Couldn't respawn", npc, "during", event, "at", coord));
continue; continue;
} }
Messaging.idebug(() -> Joiner.on(' ').join("Spawned", npc, "during", event, "at", coord)); Messaging.idebug(() -> Joiner.on(' ').join("Spawned", npc, "during", event, "at", coord));
} }
for (NPC npc : ids) { for (NPC npc : ids) {
@ -895,16 +873,16 @@ public class EventListen implements Listener {
} }
void unloadNPCs(ChunkEvent event, List<Entity> entities) { void unloadNPCs(ChunkEvent event, List<Entity> entities) {
final List<NPC> toDespawn = Lists.newArrayList(); List<NPC> toDespawn = Lists.newArrayList();
for (Entity entity : entities) { for (Entity entity : entities) {
NPC npc = CitizensAPI.getNPCRegistry().getNPC(entity); NPC npc = CitizensAPI.getNPCRegistry().getNPC(entity);
// XXX : npc#isSpawned() checks entity valid status which is now inconsistent on chunk unload between // XXX : npc#isSpawned() checks entity valid status which is now inconsistent on chunk unload between
// different server software (e.g. Paper and Spigot), so check for npc.getEntity() == null instead. // different server software (e.g. Paper and Spigot), so check for npc.getEntity() == null instead.
if (npc == null || npc.getEntity() == null) if (npc == null || npc.getEntity() == null) {
continue; continue;
}
toDespawn.add(npc); toDespawn.add(npc);
} }
if (toDespawn.isEmpty()) if (toDespawn.isEmpty())
return; return;
@ -918,7 +896,6 @@ public class EventListen implements Listener {
toRespawn.put(coord, npc); toRespawn.put(coord, npc);
continue; continue;
} }
((Cancellable) event).setCancelled(true); ((Cancellable) event).setCancelled(true);
Messaging.debug("Cancelled chunk unload at", coord); Messaging.debug("Cancelled chunk unload at", coord);
respawnAllFromCoord(coord, event); respawnAllFromCoord(coord, event);
@ -930,7 +907,6 @@ public class EventListen implements Listener {
if (Messaging.isDebugging() && Setting.DEBUG_CHUNK_LOADS.asBoolean()) { if (Messaging.isDebugging() && Setting.DEBUG_CHUNK_LOADS.asBoolean()) {
new Exception("CITIZENS CHUNK UNLOAD DEBUG " + coord).printStackTrace(); new Exception("CITIZENS CHUNK UNLOAD DEBUG " + coord).printStackTrace();
} }
if (loadChunk) { if (loadChunk) {
Messaging.idebug(() -> Joiner.on(' ').join("Loading chunk in 10 ticks due to forced chunk load at", coord)); Messaging.idebug(() -> Joiner.on(' ').join("Loading chunk in 10 ticks due to forced chunk load at", coord));
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> {

View File

@ -91,7 +91,7 @@ public class Metrics {
metricsBase = new MetricsBase("bukkit", serverUUID, serviceId, enabled, this::appendPlatformData, metricsBase = new MetricsBase("bukkit", serverUUID, serviceId, enabled, this::appendPlatformData,
this::appendServiceData, submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), this::appendServiceData, submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
plugin::isEnabled, (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), plugin::isEnabled, (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
(message) -> this.plugin.getLogger().log(Level.INFO, message), logErrors, logSentData, message -> this.plugin.getLogger().log(Level.INFO, message), logErrors, logSentData,
logResponseStatusText); logResponseStatusText);
} }
@ -156,10 +156,9 @@ public class Metrics {
protected JsonObjectBuilder.JsonObject getChartData() throws Exception { protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, int[]> map = callable.call(); Map<String, int[]> map = callable.call();
if (map == null || map.isEmpty()) { if (map == null || map.isEmpty())
// Null = skip the chart // Null = skip the chart
return null; return null;
}
boolean allSkipped = true; boolean allSkipped = true;
for (Map.Entry<String, int[]> entry : map.entrySet()) { for (Map.Entry<String, int[]> entry : map.entrySet()) {
if (entry.getValue().length == 0) { if (entry.getValue().length == 0) {
@ -169,10 +168,9 @@ public class Metrics {
allSkipped = false; allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue()); valuesBuilder.appendField(entry.getKey(), entry.getValue());
} }
if (allSkipped) { if (allSkipped)
// Null = skip the chart // Null = skip the chart
return null; return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
} }
} }
@ -198,10 +196,9 @@ public class Metrics {
protected JsonObjectBuilder.JsonObject getChartData() throws Exception { protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call(); Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) { if (map == null || map.isEmpty())
// Null = skip the chart // Null = skip the chart
return null; return null;
}
boolean allSkipped = true; boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) { for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) { if (entry.getValue() == 0) {
@ -211,10 +208,9 @@ public class Metrics {
allSkipped = false; allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue()); valuesBuilder.appendField(entry.getKey(), entry.getValue());
} }
if (allSkipped) { if (allSkipped)
// Null = skip the chart // Null = skip the chart
return null; return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
} }
} }
@ -224,9 +220,8 @@ public class Metrics {
private final String chartId; private final String chartId;
protected CustomChart(String chartId) { protected CustomChart(String chartId) {
if (chartId == null) { if (chartId == null)
throw new IllegalArgumentException("chartId must not be null"); throw new IllegalArgumentException("chartId must not be null");
}
this.chartId = chartId; this.chartId = chartId;
} }
@ -238,10 +233,9 @@ public class Metrics {
builder.appendField("chartId", chartId); builder.appendField("chartId", chartId);
try { try {
JsonObjectBuilder.JsonObject data = getChartData(); JsonObjectBuilder.JsonObject data = getChartData();
if (data == null) { if (data == null)
// If the data is null we don't send the chart. // If the data is null we don't send the chart.
return null; return null;
}
builder.appendField("data", data); builder.appendField("data", data);
} catch (Throwable t) { } catch (Throwable t) {
if (logErrors) { if (logErrors) {
@ -274,10 +268,9 @@ public class Metrics {
public JsonObjectBuilder.JsonObject getChartData() throws Exception { public JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Map<String, Integer>> map = callable.call(); Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) { if (map == null || map.isEmpty())
// Null = skip the chart // Null = skip the chart
return null; return null;
}
boolean reallyAllSkipped = true; boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) { for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
@ -291,10 +284,9 @@ public class Metrics {
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
} }
} }
if (reallyAllSkipped) { if (reallyAllSkipped)
// Null = skip the chart // Null = skip the chart
return null; return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
} }
} }
@ -339,9 +331,8 @@ public class Metrics {
* @return A reference to this object. * @return A reference to this object.
*/ */
public JsonObjectBuilder appendField(String key, int[] values) { public JsonObjectBuilder appendField(String key, int[] values) {
if (values == null) { if (values == null)
throw new IllegalArgumentException("JSON values must not be null"); throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues = Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); String escapedValues = Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]"); appendFieldUnescaped(key, "[" + escapedValues + "]");
return this; return this;
@ -357,9 +348,8 @@ public class Metrics {
* @return A reference to this object. * @return A reference to this object.
*/ */
public JsonObjectBuilder appendField(String key, JsonObject object) { public JsonObjectBuilder appendField(String key, JsonObject object) {
if (object == null) { if (object == null)
throw new IllegalArgumentException("JSON object must not be null"); throw new IllegalArgumentException("JSON object must not be null");
}
appendFieldUnescaped(key, object.toString()); appendFieldUnescaped(key, object.toString());
return this; return this;
} }
@ -374,9 +364,8 @@ public class Metrics {
* @return A reference to this object. * @return A reference to this object.
*/ */
public JsonObjectBuilder appendField(String key, JsonObject[] values) { public JsonObjectBuilder appendField(String key, JsonObject[] values) {
if (values == null) { if (values == null)
throw new IllegalArgumentException("JSON values must not be null"); throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues = Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); String escapedValues = Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]"); appendFieldUnescaped(key, "[" + escapedValues + "]");
return this; return this;
@ -392,9 +381,8 @@ public class Metrics {
* @return A reference to this object. * @return A reference to this object.
*/ */
public JsonObjectBuilder appendField(String key, String value) { public JsonObjectBuilder appendField(String key, String value) {
if (value == null) { if (value == null)
throw new IllegalArgumentException("JSON value must not be null"); throw new IllegalArgumentException("JSON value must not be null");
}
appendFieldUnescaped(key, "\"" + escape(value) + "\""); appendFieldUnescaped(key, "\"" + escape(value) + "\"");
return this; return this;
} }
@ -409,9 +397,8 @@ public class Metrics {
* @return A reference to this object. * @return A reference to this object.
*/ */
public JsonObjectBuilder appendField(String key, String[] values) { public JsonObjectBuilder appendField(String key, String[] values) {
if (values == null) { if (values == null)
throw new IllegalArgumentException("JSON values must not be null"); throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues = Arrays.stream(values).map(value -> "\"" + escape(value) + "\"") String escapedValues = Arrays.stream(values).map(value -> "\"" + escape(value) + "\"")
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]"); appendFieldUnescaped(key, "[" + escapedValues + "]");
@ -427,12 +414,10 @@ public class Metrics {
* The escaped value of the field. * The escaped value of the field.
*/ */
private void appendFieldUnescaped(String key, String escapedValue) { private void appendFieldUnescaped(String key, String escapedValue) {
if (builder == null) { if (builder == null)
throw new IllegalStateException("JSON has already been built"); throw new IllegalStateException("JSON has already been built");
} if (key == null)
if (key == null) {
throw new IllegalArgumentException("JSON key must not be null"); throw new IllegalArgumentException("JSON key must not be null");
}
if (hasAtLeastOneField) { if (hasAtLeastOneField) {
builder.append(","); builder.append(",");
} }
@ -458,9 +443,8 @@ public class Metrics {
* @return The built JSON string. * @return The built JSON string.
*/ */
public JsonObject build() { public JsonObject build() {
if (builder == null) { if (builder == null)
throw new IllegalStateException("JSON has already been built"); throw new IllegalStateException("JSON has already been built");
}
JsonObject object = new JsonObject(builder.append("}").toString()); JsonObject object = new JsonObject(builder.append("}").toString());
builder = null; builder = null;
return object; return object;
@ -499,7 +483,7 @@ public class Metrics {
* @return The escaped value. * @return The escaped value.
*/ */
private static String escape(String value) { private static String escape(String value) {
final StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (int i = 0; i < value.length(); i++) { for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i); char c = value.charAt(i);
if (c == '"') { if (c == '"') {
@ -605,7 +589,7 @@ public class Metrics {
} }
public void addCustomChart(CustomChart chart) { public void addCustomChart(CustomChart chart) {
this.customCharts.add(chart); customCharts.add(chart);
} }
/** Checks that the class was properly relocated. */ /** Checks that the class was properly relocated. */
@ -615,16 +599,14 @@ public class Metrics {
|| !System.getProperty("bstats.relocatecheck").equals("false")) { || !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this little // Maven's Relocate is clever and changes strings, too. So we have to use this little
// "trick" ... :D // "trick" ... :D
final String defaultPackage = new String( String defaultPackage = new String(new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's' });
new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's' }); String examplePackage = new String(
final String examplePackage = new String(
new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' }); new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' });
// We want to make sure no one just copy & pastes the example and uses the wrong package // We want to make sure no one just copy & pastes the example and uses the wrong package
// names // names
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { || MetricsBase.class.getPackage().getName().startsWith(examplePackage))
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
} }
} }
@ -661,7 +643,7 @@ public class Metrics {
} }
private void startSubmitting() { private void startSubmitting() {
final Runnable submitTask = () -> { Runnable submitTask = () -> {
if (!enabled || !checkServiceEnabledSupplier.get()) { if (!enabled || !checkServiceEnabledSupplier.get()) {
// Submitting data or service is disabled // Submitting data or service is disabled
scheduler.shutdown(); scheduler.shutdown();
@ -670,7 +652,7 @@ public class Metrics {
if (submitTaskConsumer != null) { if (submitTaskConsumer != null) {
submitTaskConsumer.accept(this::submitData); submitTaskConsumer.accept(this::submitData);
} else { } else {
this.submitData(); submitData();
} }
}; };
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution
@ -688,9 +670,9 @@ public class Metrics {
} }
private void submitData() { private void submitData() {
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
appendPlatformDataConsumer.accept(baseJsonBuilder); appendPlatformDataConsumer.accept(baseJsonBuilder);
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
appendServiceDataConsumer.accept(serviceJsonBuilder); appendServiceDataConsumer.accept(serviceJsonBuilder);
JsonObjectBuilder.JsonObject[] chartData = customCharts.stream() JsonObjectBuilder.JsonObject[] chartData = customCharts.stream()
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
@ -721,10 +703,9 @@ public class Metrics {
* The string to gzip. * The string to gzip.
* @return The gzipped string. * @return The gzipped string.
*/ */
private static byte[] compress(final String str) throws IOException { private static byte[] compress(String str) throws IOException {
if (str == null) { if (str == null)
return null; return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8)); gzip.write(str.getBytes(StandardCharsets.UTF_8));
@ -733,11 +714,11 @@ public class Metrics {
} }
/** The version of the Metrics class. */ /** The version of the Metrics class. */
public static final String METRICS_VERSION = "3.0.0"; public static String METRICS_VERSION = "3.0.0";
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; private static String REPORT_URL = "https://bStats.org/api/v2/data/%s";
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1,
task -> new Thread(task, "bStats-Metrics")); task -> new Thread(task, "bStats-Metrics"));
} }
@ -762,10 +743,9 @@ public class Metrics {
protected JsonObjectBuilder.JsonObject getChartData() throws Exception { protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call(); Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) { if (map == null || map.isEmpty())
// Null = skip the chart // Null = skip the chart
return null; return null;
}
boolean allSkipped = true; boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) { for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) { if (entry.getValue() == 0) {
@ -775,10 +755,9 @@ public class Metrics {
allSkipped = false; allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue()); valuesBuilder.appendField(entry.getKey(), entry.getValue());
} }
if (allSkipped) { if (allSkipped)
// Null = skip the chart // Null = skip the chart
return null; return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
} }
} }
@ -804,10 +783,9 @@ public class Metrics {
protected JsonObjectBuilder.JsonObject getChartData() throws Exception { protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call(); Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) { if (map == null || map.isEmpty())
// Null = skip the chart // Null = skip the chart
return null; return null;
}
for (Map.Entry<String, Integer> entry : map.entrySet()) { for (Map.Entry<String, Integer> entry : map.entrySet()) {
valuesBuilder.appendField(entry.getKey(), new int[] { entry.getValue() }); valuesBuilder.appendField(entry.getKey(), new int[] { entry.getValue() });
} }
@ -835,10 +813,9 @@ public class Metrics {
@Override @Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception { protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
String value = callable.call(); String value = callable.call();
if (value == null || value.isEmpty()) { if (value == null || value.isEmpty())
// Null = skip the chart // Null = skip the chart
return null; return null;
}
return new JsonObjectBuilder().appendField("value", value).build(); return new JsonObjectBuilder().appendField("value", value).build();
} }
} }
@ -863,10 +840,9 @@ public class Metrics {
@Override @Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception { protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
int value = callable.call(); int value = callable.call();
if (value == 0) { if (value == 0)
// Null = skip the chart // Null = skip the chart
return null; return null;
}
return new JsonObjectBuilder().appendField("value", value).build(); return new JsonObjectBuilder().appendField("value", value).build();
} }
} }

View File

@ -11,7 +11,7 @@ public class NPCNeedsRespawnEvent extends NPCEvent {
public NPCNeedsRespawnEvent(NPC npc, Location at) { public NPCNeedsRespawnEvent(NPC npc, Location at) {
super(npc); super(npc);
this.spawn = at; spawn = at;
} }
@Override @Override
@ -27,5 +27,5 @@ public class NPCNeedsRespawnEvent extends NPCEvent {
return handlers; return handlers;
} }
private static final HandlerList handlers = new HandlerList(); private static HandlerList handlers = new HandlerList();
} }

View File

@ -48,6 +48,8 @@ import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.trait.MobType; import net.citizensnpcs.api.trait.trait.MobType;
import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.npc.ai.NPCHolder;
import net.citizensnpcs.trait.ClickRedirectTrait;
import net.citizensnpcs.trait.HologramTrait;
import net.citizensnpcs.trait.MirrorTrait; import net.citizensnpcs.trait.MirrorTrait;
import net.citizensnpcs.trait.RotationTrait; import net.citizensnpcs.trait.RotationTrait;
import net.citizensnpcs.trait.RotationTrait.PacketRotationSession; import net.citizensnpcs.trait.RotationTrait.PacketRotationSession;
@ -55,15 +57,15 @@ import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.SkinProperty; import net.citizensnpcs.util.SkinProperty;
public class ProtocolLibListener implements Listener { public class ProtocolLibListener implements Listener {
private final Class<?> flagsClass; private Class<?> flagsClass;
private final ProtocolManager manager; private ProtocolManager manager;
private final Map<UUID, MirrorTrait> mirrorTraits = Maps.newConcurrentMap(); private final Map<UUID, MirrorTrait> mirrorTraits = Maps.newConcurrentMap();
private final Citizens plugin; private Citizens plugin;
private final Map<Integer, RotationTrait> rotationTraits = Maps.newConcurrentMap(); private final Map<Integer, RotationTrait> rotationTraits = Maps.newConcurrentMap();
public ProtocolLibListener(Citizens plugin) { public ProtocolLibListener(Citizens plugin) {
this.plugin = plugin; this.plugin = plugin;
this.manager = ProtocolLibrary.getProtocolManager(); manager = ProtocolLibrary.getProtocolManager();
flagsClass = MinecraftReflection.getMinecraftClass("RelativeMovement", "world.entity.RelativeMovement", flagsClass = MinecraftReflection.getMinecraftClass("RelativeMovement", "world.entity.RelativeMovement",
"EnumPlayerTeleportFlags", "PacketPlayOutPosition$EnumPlayerTeleportFlags", "EnumPlayerTeleportFlags", "PacketPlayOutPosition$EnumPlayerTeleportFlags",
"network.protocol.game.PacketPlayOutPosition$EnumPlayerTeleportFlags"); "network.protocol.game.PacketPlayOutPosition$EnumPlayerTeleportFlags");
@ -72,50 +74,59 @@ public class ProtocolLibListener implements Listener {
@Override @Override
public void onPacketSending(PacketEvent event) { public void onPacketSending(PacketEvent event) {
NPC npc = getNPCFromPacket(event); NPC npc = getNPCFromPacket(event);
if (npc == null || !npc.data().has(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER)) if (npc == null)
return; return;
Function<Player, String> hvs = npc.data().get(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER);
int version = manager.getProtocolVersion(event.getPlayer());
PacketContainer packet = event.getPacket(); PacketContainer packet = event.getPacket();
if (version < 761) { int version = manager.getProtocolVersion(event.getPlayer());
List<WrappedWatchableObject> wwo = packet.getWatchableCollectionModifier().readSafely(0); if (npc.data().has(NPC.Metadata.HOLOGRAM_FOR) || npc.data().has(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER)) {
if (wwo == null) Function<Player, String> hvs = npc.data().get(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER);
return; Object fakeName = null;
if (hvs != null) {
String suppliedName = hvs.apply(event.getPlayer());
fakeName = version <= 340 ? suppliedName
: Optional.of(Messaging.minecraftComponentFromRawMessage(suppliedName));
}
boolean sneaking = npc.getOrAddTrait(ClickRedirectTrait.class).getRedirectNPC()
.getOrAddTrait(HologramTrait.class).isHologramSneaking(npc, event.getPlayer());
boolean delta = false; boolean delta = false;
String text = hvs.apply(event.getPlayer());
for (WrappedWatchableObject wo : wwo) { if (version < 761) {
if (wo.getIndex() != 2) List<WrappedWatchableObject> wwo = packet.getWatchableCollectionModifier().readSafely(0);
continue; if (wwo == null)
if (version <= 340) { return;
wo.setValue(text);
} else { for (WrappedWatchableObject wo : wwo) {
wo.setValue(Optional.of(Messaging.minecraftComponentFromRawMessage(text))); if (fakeName != null && wo.getIndex() == 2) {
wo.setValue(fakeName);
delta = true;
} else if (sneaking && wo.getIndex() == 0) {
byte b = (byte) (((Number) wo.getValue()).byteValue() | 0x02);
wo.setValue(b);
delta = true;
}
} }
delta = true; if (delta) {
break; packet.getWatchableCollectionModifier().write(0, wwo);
} }
} else {
List<WrappedDataValue> wdvs = packet.getDataValueCollectionModifier().readSafely(0);
if (wdvs == null)
return;
if (delta) { for (WrappedDataValue wdv : wdvs) {
packet.getWatchableCollectionModifier().write(0, wwo); if (fakeName != null && wdv.getIndex() == 2) {
} wdv.setValue(fakeName);
} else { delta = true;
List<WrappedDataValue> wdvs = packet.getDataValueCollectionModifier().readSafely(0); } else if (sneaking && wdv.getIndex() == 0) {
if (wdvs == null) byte b = (byte) (((Number) wdv.getValue()).byteValue() | 0x02);
return; wdv.setValue(b);
delta = true;
boolean delta = false; }
String text = hvs.apply(event.getPlayer()); }
for (WrappedDataValue wdv : wdvs) { if (delta) {
if (wdv.getIndex() != 2) packet.getDataValueCollectionModifier().write(0, wdvs);
continue; }
wdv.setValue(Optional.of(Messaging.minecraftComponentFromRawMessage(text)));
break;
}
if (delta) {
packet.getDataValueCollectionModifier().write(0, wdvs);
} }
} }
} }
@ -130,7 +141,6 @@ public class ProtocolLibListener implements Listener {
uuid -> mirrorTraits.get(uuid)); uuid -> mirrorTraits.get(uuid));
return; return;
} }
List<PlayerInfoData> list = event.getPacket().getPlayerInfoDataLists().readSafely(0); List<PlayerInfoData> list = event.getPacket().getPlayerInfoDataLists().readSafely(0);
if (list == null) if (list == null)
return; return;
@ -153,13 +163,11 @@ public class ProtocolLibListener implements Listener {
wgp = WrappedGameProfile.fromPlayer(event.getPlayer()); wgp = WrappedGameProfile.fromPlayer(event.getPlayer());
playerName = WrappedChatComponent.fromText(event.getPlayer().getDisplayName()); playerName = WrappedChatComponent.fromText(event.getPlayer().getDisplayName());
} }
if (trait.mirrorName()) { if (trait.mirrorName()) {
list.set(i, new PlayerInfoData(wgp.withId(npcInfo.getProfile().getId()), npcInfo.getLatency(), list.set(i, new PlayerInfoData(wgp.withId(npcInfo.getProfile().getId()), npcInfo.getLatency(),
npcInfo.getGameMode(), playerName)); npcInfo.getGameMode(), playerName));
continue; continue;
} }
Collection<Property> textures = playerProfile.getProperties().get("textures"); Collection<Property> textures = playerProfile.getProperties().get("textures");
if (textures == null || textures.size() == 0) if (textures == null || textures.size() == 0)
continue; continue;
@ -174,7 +182,6 @@ public class ProtocolLibListener implements Listener {
} }
changed = true; changed = true;
} }
if (changed) { if (changed) {
event.getPacket().getPlayerInfoDataLists().write(0, list); event.getPacket().getPlayerInfoDataLists().write(0, list);
} }
@ -202,7 +209,6 @@ public class ProtocolLibListener implements Listener {
} }
return; return;
} }
RotationTrait trait = rotationTraits.get(eid); RotationTrait trait = rotationTraits.get(eid);
if (trait == null) if (trait == null)
return; return;
@ -215,10 +221,8 @@ public class ProtocolLibListener implements Listener {
PacketType type = event.getPacketType(); PacketType type = event.getPacketType();
if (type == Server.ENTITY_HEAD_ROTATION) { if (type == Server.ENTITY_HEAD_ROTATION) {
packet.getBytes().write(0, degToByte(session.getHeadYaw())); packet.getBytes().write(0, degToByte(session.getHeadYaw()));
} else if (type == Server.ENTITY_LOOK) { } else if (type == Server.ENTITY_LOOK || type == Server.ENTITY_MOVE_LOOK
packet.getBytes().write(0, degToByte(session.getBodyYaw())); || type == Server.REL_ENTITY_MOVE_LOOK) {
packet.getBytes().write(1, degToByte(session.getPitch()));
} else if (type == Server.ENTITY_MOVE_LOOK || type == Server.REL_ENTITY_MOVE_LOOK) {
packet.getBytes().write(0, degToByte(session.getBodyYaw())); packet.getBytes().write(0, degToByte(session.getBodyYaw()));
packet.getBytes().write(1, degToByte(session.getPitch())); packet.getBytes().write(1, degToByte(session.getPitch()));
} else if (type == Server.POSITION) { } else if (type == Server.POSITION) {
@ -231,7 +235,6 @@ public class ProtocolLibListener implements Listener {
packet.getFloat().write(0, session.getBodyYaw()); packet.getFloat().write(0, session.getBodyYaw());
packet.getFloat().write(1, session.getPitch()); packet.getFloat().write(1, session.getPitch());
} }
session.onPacketOverwritten(); session.onPacketOverwritten();
Messaging.debug("OVERWRITTEN " + type + " " + packet.getHandle()); Messaging.debug("OVERWRITTEN " + type + " " + packet.getHandle());
} }
@ -258,7 +261,6 @@ public class ProtocolLibListener implements Listener {
} }
return null; return null;
} }
return entity instanceof NPCHolder ? ((NPCHolder) entity).getNPC() : null; return entity instanceof NPCHolder ? ((NPCHolder) entity).getNPC() : null;
} }
@ -278,7 +280,6 @@ public class ProtocolLibListener implements Listener {
rotationTraits.put(event.getNPC().getEntity().getEntityId(), rotationTraits.put(event.getNPC().getEntity().getEntityId(),
event.getNPC().getTraitNullable(RotationTrait.class)); event.getNPC().getTraitNullable(RotationTrait.class));
} }
if (event.getNPC().hasTrait(MirrorTrait.class) if (event.getNPC().hasTrait(MirrorTrait.class)
&& event.getNPC().getOrAddTrait(MobType.class).getType() == EntityType.PLAYER) { && event.getNPC().getOrAddTrait(MobType.class).getType() == EntityType.PLAYER) {
mirrorTraits.put(event.getNPC().getEntity().getUniqueId(), mirrorTraits.put(event.getNPC().getEntity().getUniqueId(),

View File

@ -147,7 +147,7 @@ public class Settings {
DEFAULT_TEXT("npc.default.talk-close.text.0", "Hi, I'm <npc>!") { DEFAULT_TEXT("npc.default.talk-close.text.0", "Hi, I'm <npc>!") {
@Override @Override
public void loadFromKey(DataKey root) { public void loadFromKey(DataKey root) {
List<String> list = new ArrayList<String>(); List<String> list = new ArrayList<>();
for (DataKey key : root.getRelative("npc.default.talk-close.text").getSubKeys()) { for (DataKey key : root.getRelative("npc.default.talk-close.text").getSubKeys()) {
list.add(key.getString("")); list.add(key.getString(""));
} }
@ -290,7 +290,7 @@ public class Settings {
if (migrate.contains(".")) { if (migrate.contains(".")) {
this.migrate = migrate; this.migrate = migrate;
} else { } else {
this.comments = migrate; comments = migrate;
} }
this.path = path; this.path = path;
this.value = value; this.value = value;
@ -316,9 +316,8 @@ public class Settings {
} }
public int asInt() { public int asInt() {
if (value instanceof String) { if (value instanceof String)
return Integer.parseInt(value.toString()); return Integer.parseInt(value.toString());
}
return ((Number) value).intValue(); return ((Number) value).intValue();
} }

View File

@ -38,9 +38,8 @@ public class StoredShops {
} }
public NPCShop getShop(String name) { public NPCShop getShop(String name) {
if (npcShops.containsKey(name)) { if (npcShops.containsKey(name))
return npcShops.get(name); return npcShops.get(name);
}
return getGlobalShop(name); return getGlobalShop(name);
} }

View File

@ -20,7 +20,7 @@ import net.citizensnpcs.util.StringHelper;
@Requirements @Requirements
public class AdminCommands { public class AdminCommands {
private final Citizens plugin; private final Citizens plugin;
private final Map<CommandSender, Long> reloadTimeouts = new WeakHashMap<CommandSender, Long>(); private final Map<CommandSender, Long> reloadTimeouts = new WeakHashMap<>();
public AdminCommands(Citizens plugin) { public AdminCommands(Citizens plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -51,7 +51,6 @@ public class AdminCommands {
return; return;
} }
} }
Messaging.sendTr(sender, Messages.CITIZENS_RELOADING); Messaging.sendTr(sender, Messages.CITIZENS_RELOADING);
try { try {
plugin.reload(); plugin.reload();

View File

@ -60,7 +60,6 @@ public class EditorCommands {
player.acceptConversationInput(args.getJoinedStrings(1)); player.acceptConversationInput(args.getJoinedStrings(1));
return; return;
} }
Editor.enterOrLeave(player, editor); Editor.enterOrLeave(player, editor);
} }

View File

@ -34,7 +34,7 @@ public class NPCCommandSelector extends NumericPrompt {
public NPCCommandSelector(Callback callback, List<NPC> possible) { public NPCCommandSelector(Callback callback, List<NPC> possible) {
this.callback = callback; this.callback = callback;
this.choices = possible; choices = possible;
} }
@Override @Override
@ -86,7 +86,7 @@ public class NPCCommandSelector extends NumericPrompt {
} }
public static void start(Callback callback, Conversable player, List<NPC> possible) { public static void start(Callback callback, Conversable player, List<NPC> possible) {
final Conversation conversation = new ConversationFactory(CitizensAPI.getPlugin()).withLocalEcho(false) Conversation conversation = new ConversationFactory(CitizensAPI.getPlugin()).withLocalEcho(false)
.withEscapeSequence("exit").withModality(false) .withEscapeSequence("exit").withModality(false)
.withFirstPrompt(new NPCCommandSelector(callback, possible)).buildConversation(player); .withFirstPrompt(new NPCCommandSelector(callback, possible)).buildConversation(player);
conversation.begin(); conversation.begin();
@ -100,7 +100,6 @@ public class NPCCommandSelector extends NumericPrompt {
return; return;
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
Integer id = Ints.tryParse(raw); Integer id = Ints.tryParse(raw);
if (id != null) { if (id != null) {
callback.run(npcRegistry.getById(id)); callback.run(npcRegistry.getById(id));
@ -112,12 +111,12 @@ public class NPCCommandSelector extends NumericPrompt {
if (args.hasValueFlag("range")) { if (args.hasValueFlag("range")) {
range = Math.abs(args.getFlagDouble("range")); range = Math.abs(args.getFlagDouble("range"));
} }
for (NPC test : npcRegistry) { for (NPC test : npcRegistry) {
if (test.getName().equalsIgnoreCase(name)) { if (test.getName().equalsIgnoreCase(name)) {
if (range > 0 && test.isSpawned() if (range > 0 && test.isSpawned()
&& !Util.locationWithinRange(args.getSenderLocation(), test.getEntity().getLocation(), range)) && !Util.locationWithinRange(args.getSenderLocation(), test.getEntity().getLocation(), range)) {
continue; continue;
}
possible.add(test); possible.add(test);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,6 @@ public class TraitCommands {
failed.add(String.format("%s: No permission", traitName)); failed.add(String.format("%s: No permission", traitName));
continue; continue;
} }
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitName); Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitName);
if (clazz == null) { if (clazz == null) {
failed.add(String.format("%s: Trait not found", traitName)); failed.add(String.format("%s: Trait not found", traitName));
@ -108,7 +107,6 @@ public class TraitCommands {
failed.add(String.format("%s: No permission", traitName)); failed.add(String.format("%s: No permission", traitName));
continue; continue;
} }
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitName); Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitName);
if (clazz == null) { if (clazz == null) {
failed.add(String.format("%s: Trait not found", traitName)); failed.add(String.format("%s: Trait not found", traitName));
@ -122,10 +120,12 @@ public class TraitCommands {
removeTrait(npc, clazz, sender); removeTrait(npc, clazz, sender);
removed.add(StringHelper.wrap(traitName)); removed.add(StringHelper.wrap(traitName));
} }
if (removed.size() > 0) if (removed.size() > 0) {
Messaging.sendTr(sender, Messages.TRAITS_REMOVED, Joiner.on(", ").join(removed)); Messaging.sendTr(sender, Messages.TRAITS_REMOVED, Joiner.on(", ").join(removed));
if (failed.size() > 0) }
if (failed.size() > 0) {
Messaging.sendTr(sender, Messages.FAILED_TO_REMOVE, Joiner.on(", ").join(failed)); Messaging.sendTr(sender, Messages.FAILED_TO_REMOVE, Joiner.on(", ").join(failed));
}
} }
private void removeTrait(NPC npc, Class<? extends Trait> clazz, CommandSender sender) { private void removeTrait(NPC npc, Class<? extends Trait> clazz, CommandSender sender) {
@ -150,7 +150,6 @@ public class TraitCommands {
failed.add(String.format("%s: No permission", traitName)); failed.add(String.format("%s: No permission", traitName));
continue; continue;
} }
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitName); Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitName);
if (clazz == null) { if (clazz == null) {
failed.add(String.format("%s: Trait not found", traitName)); failed.add(String.format("%s: Trait not found", traitName));

View File

@ -130,8 +130,8 @@ public class WaypointCommands {
waypoints.describeProviders(sender); waypoints.describeProviders(sender);
return; return;
} }
if (sender instanceof Player && Editor.hasEditor(((Player) sender))) { if (sender instanceof Player && Editor.hasEditor((Player) sender)) {
Editor.leave(((Player) sender)); Editor.leave((Player) sender);
} }
boolean success = waypoints.setWaypointProvider(args.getString(1)); boolean success = waypoints.setWaypointProvider(args.getString(1));
if (!success) if (!success)

View File

@ -20,7 +20,7 @@ import net.citizensnpcs.util.Util;
@Menu(title = "Configure NPC", type = InventoryType.CHEST, dimensions = { 5, 9 }) @Menu(title = "Configure NPC", type = InventoryType.CHEST, dimensions = { 5, 9 })
public class NPCConfigurator extends InventoryMenuPage { public class NPCConfigurator extends InventoryMenuPage {
private final NPC npc; private NPC npc;
private NPCConfigurator() { private NPCConfigurator() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -36,7 +36,7 @@ public class NPCConfigurator extends InventoryMenuPage {
ConfiguratorInfo info = entry.getValue(); ConfiguratorInfo info = entry.getValue();
InventoryMenuSlot slot = ctx.getSlot(entry.getKey()); InventoryMenuSlot slot = ctx.getSlot(entry.getKey());
slot.setItemStack(new ItemStack(info.material, 1)); slot.setItemStack(new ItemStack(info.material, 1));
slot.setClickHandler((evt) -> info.clickHandler.accept(new ConfiguratorEvent(ctx, npc, slot, evt))); slot.setClickHandler(evt -> info.clickHandler.accept(new ConfiguratorEvent(ctx, npc, slot, evt)));
info.clickHandler.accept(new ConfiguratorEvent(ctx, npc, slot, null)); info.clickHandler.accept(new ConfiguratorEvent(ctx, npc, slot, null));
} }
} }
@ -51,7 +51,7 @@ public class NPCConfigurator extends InventoryMenuPage {
this.ctx = ctx; this.ctx = ctx;
this.npc = npc; this.npc = npc;
this.slot = slot; this.slot = slot;
this.event = evt; event = evt;
} }
} }
@ -60,18 +60,18 @@ public class NPCConfigurator extends InventoryMenuPage {
private final Material material; private final Material material;
public ConfiguratorInfo(Material mat, Consumer<ConfiguratorEvent> con) { public ConfiguratorInfo(Material mat, Consumer<ConfiguratorEvent> con) {
this.material = mat; material = mat;
this.clickHandler = con; clickHandler = con;
} }
} }
private static final Map<Integer, ConfiguratorInfo> SLOT_MAP = Maps.newHashMap(); private static final Map<Integer, ConfiguratorInfo> SLOT_MAP = Maps.newHashMap();
static { static {
SLOT_MAP.put(0, new ConfiguratorInfo(Util.getFallbackMaterial("OAK_SIGN", "SIGN"), (evt) -> { SLOT_MAP.put(0, new ConfiguratorInfo(Util.getFallbackMaterial("OAK_SIGN", "SIGN"), evt -> {
evt.slot.setDescription("Edit NPC name\n" + evt.npc.getName()); evt.slot.setDescription("Edit NPC name\n" + evt.npc.getName());
if (evt.event != null) { if (evt.event != null) {
evt.ctx.getMenu().transition( evt.ctx.getMenu()
InputMenus.stringSetter(() -> evt.npc.getName(), (input) -> evt.npc.setName(input))); .transition(InputMenus.stringSetter(() -> evt.npc.getName(), input -> evt.npc.setName(input)));
} }
})); }));
} }

View File

@ -12,7 +12,7 @@ public class CreateNPCHistoryItem implements CommandHistoryItem {
private final UUID uuid; private final UUID uuid;
public CreateNPCHistoryItem(NPC npc) { public CreateNPCHistoryItem(NPC npc) {
this.uuid = npc.getUniqueId(); uuid = npc.getUniqueId();
} }
@Override @Override

View File

@ -19,11 +19,11 @@ public class RemoveNPCHistoryItem implements CommandHistoryItem {
private final UUID uuid; private final UUID uuid;
public RemoveNPCHistoryItem(NPC from) { public RemoveNPCHistoryItem(NPC from) {
this.key = new MemoryDataKey(); key = new MemoryDataKey();
from.save(key); from.save(key);
this.type = from.getOrAddTrait(MobType.class).getType(); type = from.getOrAddTrait(MobType.class).getType();
this.uuid = from.getUniqueId(); uuid = from.getUniqueId();
this.id = from.getId(); id = from.getId();
} }
@Override @Override

View File

@ -19,7 +19,7 @@ public class CopierEditor extends Editor {
public CopierEditor(Player player, NPC npc) { public CopierEditor(Player player, NPC npc) {
this.player = player; this.player = player;
this.npc = npc; this.npc = npc;
this.name = npc.getRawName(); name = npc.getRawName();
} }
@Override @Override
@ -41,7 +41,6 @@ public class CopierEditor extends Editor {
if (!copy.getRawName().equals(name)) { if (!copy.getRawName().equals(name)) {
copy.setName(name); copy.setName(name);
} }
if (copy.isSpawned() && player.isOnline()) { if (copy.isSpawned() && player.isOnline()) {
Location location = event.getClickedBlock().getLocation(); Location location = event.getClickedBlock().getLocation();
location.setYaw(player.getLocation().getYaw()); location.setYaw(player.getLocation().getYaw());
@ -50,7 +49,6 @@ public class CopierEditor extends Editor {
copy.teleport(location, TeleportCause.PLUGIN); copy.teleport(location, TeleportCause.PLUGIN);
copy.getOrAddTrait(CurrentLocation.class).setLocation(location); copy.getOrAddTrait(CurrentLocation.class).setLocation(location);
} }
Messaging.sendTr(player, Messages.NPC_COPIED, npc.getName()); Messaging.sendTr(player, Messages.NPC_COPIED, npc.getName());
event.setCancelled(true); event.setCancelled(true);
} }

View File

@ -58,5 +58,5 @@ public abstract class Editor implements Listener {
EDITING.clear(); EDITING.clear();
} }
private static final Map<UUID, Editor> EDITING = new HashMap<UUID, Editor>(); private static final Map<UUID, Editor> EDITING = new HashMap<>();
} }

View File

@ -73,32 +73,32 @@ public class GenericEquipperGUI extends InventoryMenuPage {
@ClickHandler(slot = { 1, 5 }) @ClickHandler(slot = { 1, 5 })
public void setBoots(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { public void setBoots(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
set(EquipmentSlot.BOOTS, event, (type) -> type == Material.AIR || type.name().endsWith("BOOTS")); set(EquipmentSlot.BOOTS, event, type -> type == Material.AIR || type.name().endsWith("BOOTS"));
} }
@ClickHandler(slot = { 1, 3 }) @ClickHandler(slot = { 1, 3 })
public void setChest(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { public void setChest(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
set(EquipmentSlot.CHESTPLATE, event, set(EquipmentSlot.CHESTPLATE, event,
(type) -> type == Material.AIR || type.name().endsWith("CHESTPLATE") || type.name().equals("ELYTRA")); type -> type == Material.AIR || type.name().endsWith("CHESTPLATE") || type.name().equals("ELYTRA"));
} }
@ClickHandler(slot = { 1, 0 }) @ClickHandler(slot = { 1, 0 })
public void setHand(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { public void setHand(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
set(EquipmentSlot.HAND, event, (type) -> true); set(EquipmentSlot.HAND, event, type -> true);
} }
@ClickHandler(slot = { 1, 2 }) @ClickHandler(slot = { 1, 2 })
public void setHelmet(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { public void setHelmet(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
set(EquipmentSlot.HELMET, event, (type) -> true); set(EquipmentSlot.HELMET, event, type -> true);
} }
@ClickHandler(slot = { 1, 4 }) @ClickHandler(slot = { 1, 4 })
public void setLeggings(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { public void setLeggings(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
set(EquipmentSlot.LEGGINGS, event, (type) -> type == Material.AIR || type.name().endsWith("LEGGINGS")); set(EquipmentSlot.LEGGINGS, event, type -> type == Material.AIR || type.name().endsWith("LEGGINGS"));
} }
@ClickHandler(slot = { 1, 1 }) @ClickHandler(slot = { 1, 1 })
public void setOffhand(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { public void setOffhand(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
set(EquipmentSlot.OFF_HAND, event, (type) -> true); set(EquipmentSlot.OFF_HAND, event, type -> true);
} }
} }

View File

@ -43,11 +43,10 @@ public abstract class AbstractEntityController implements EntityController {
if (bukkitEntity instanceof Player) { if (bukkitEntity instanceof Player) {
NMS.removeFromWorld(bukkitEntity); NMS.removeFromWorld(bukkitEntity);
NMS.remove(bukkitEntity); NMS.remove(bukkitEntity);
bukkitEntity = null;
} else { } else {
bukkitEntity.remove(); bukkitEntity.remove();
bukkitEntity = null;
} }
bukkitEntity = null;
} }
@Override @Override

View File

@ -82,54 +82,44 @@ public class CitizensNPC extends AbstractNPC {
} }
return true; return true;
} }
NPCDespawnEvent event = new NPCDespawnEvent(this, reason); NPCDespawnEvent event = new NPCDespawnEvent(this, reason);
if (reason == DespawnReason.CHUNK_UNLOAD) { if (reason == DespawnReason.CHUNK_UNLOAD) {
event.setCancelled(data().get(NPC.Metadata.KEEP_CHUNK_LOADED, Setting.KEEP_CHUNKS_LOADED.asBoolean())); event.setCancelled(data().get(NPC.Metadata.KEEP_CHUNK_LOADED, Setting.KEEP_CHUNKS_LOADED.asBoolean()));
} }
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled() && reason != DespawnReason.DEATH) { if (event.isCancelled() && reason != DespawnReason.DEATH) {
Messaging.debug("Couldn't despawn", this, "due to despawn event cancellation. Will load chunk.", Messaging.debug("Couldn't despawn", this, "due to despawn event cancellation. Will load chunk.",
getEntity().isValid(), ", DespawnReason." + reason); getEntity().isValid(), ", DespawnReason." + reason);
return false; return false;
} }
boolean keepSelected = getOrAddTrait(Spawned.class).shouldSpawn(); boolean keepSelected = getOrAddTrait(Spawned.class).shouldSpawn();
if (!keepSelected) { if (!keepSelected) {
data().remove("selectors"); data().remove("selectors");
} }
if (getEntity() != null) { if (getEntity() != null) {
getEntity().removeMetadata("NPC", CitizensAPI.getPlugin()); getEntity().removeMetadata("NPC", CitizensAPI.getPlugin());
getEntity().removeMetadata("NPC-ID", CitizensAPI.getPlugin()); getEntity().removeMetadata("NPC-ID", CitizensAPI.getPlugin());
} }
if (getEntity() instanceof Player) { if (getEntity() instanceof Player) {
PlayerUpdateTask.deregisterPlayer(getEntity()); PlayerUpdateTask.deregisterPlayer(getEntity());
} }
navigator.onDespawn(); navigator.onDespawn();
if (reason == DespawnReason.RELOAD) { if (reason == DespawnReason.RELOAD) {
unloadEvents(); unloadEvents();
} }
for (Trait trait : new ArrayList<>(traits.values())) {
for (Trait trait : new ArrayList<Trait>(traits.values())) {
trait.onDespawn(reason); trait.onDespawn(reason);
} }
Messaging.debug("Despawned", this, "DespawnReason." + reason); Messaging.debug("Despawned", this, "DespawnReason." + reason);
if (getEntity() instanceof SkinnableEntity) { if (getEntity() instanceof SkinnableEntity) {
((SkinnableEntity) getEntity()).getSkinTracker().onRemoveNPC(); ((SkinnableEntity) getEntity()).getSkinTracker().onRemoveNPC();
} }
if (reason == DespawnReason.DEATH) { if (reason == DespawnReason.DEATH) {
entityController.die(); entityController.die();
} else { } else {
entityController.remove(); entityController.remove();
} }
return true; return true;
} }
@ -190,7 +180,7 @@ public class CitizensNPC extends AbstractNPC {
} }
@Override @Override
public void load(final DataKey root) { public void load(DataKey root) {
super.load(root); super.load(root);
CurrentLocation spawnLocation = getOrAddTrait(CurrentLocation.class); CurrentLocation spawnLocation = getOrAddTrait(CurrentLocation.class);
@ -201,14 +191,13 @@ public class CitizensNPC extends AbstractNPC {
Messaging.debug("Tried to spawn", this, "on load but world was null"); Messaging.debug("Tried to spawn", this, "on load but world was null");
} }
} }
navigator.load(root.getRelative("navigator")); navigator.load(root.getRelative("navigator"));
} }
@Override @Override
public boolean requiresNameHologram() { public boolean requiresNameHologram() {
return super.requiresNameHologram() return super.requiresNameHologram()
|| (Setting.ALWAYS_USE_NAME_HOLOGRAM.asBoolean() && !data().has(NPC.Metadata.HOLOGRAM_FOR)); || Setting.ALWAYS_USE_NAME_HOLOGRAM.asBoolean() && !data().has(NPC.Metadata.HOLOGRAM_FOR);
} }
private void resetCachedCoord() { private void resetCachedCoord() {
@ -255,12 +244,10 @@ public class CitizensNPC extends AbstractNPC {
prev = getEntity().getLocation(); prev = getEntity().getLocation();
despawn(DespawnReason.PENDING_RESPAWN); despawn(DespawnReason.PENDING_RESPAWN);
} }
PacketNPC packet = getTraitNullable(PacketNPC.class); PacketNPC packet = getTraitNullable(PacketNPC.class);
if (packet != null) { if (packet != null) {
newController = packet.wrap(newController); newController = packet.wrap(newController);
} }
entityController = newController; entityController = newController;
if (wasSpawned) { if (wasSpawned) {
spawn(prev, SpawnReason.RESPAWN); spawn(prev, SpawnReason.RESPAWN);
@ -315,7 +302,6 @@ public class CitizensNPC extends AbstractNPC {
if (reason == SpawnReason.CHUNK_LOAD || reason == SpawnReason.COMMAND) { if (reason == SpawnReason.CHUNK_LOAD || reason == SpawnReason.COMMAND) {
at.getChunk().load(); at.getChunk().load();
} }
getOrAddTrait(CurrentLocation.class).setLocation(at); getOrAddTrait(CurrentLocation.class).setLocation(at);
entityController.create(at.clone(), this); entityController.create(at.clone(), this);
getEntity().setMetadata("NPC", new FixedMetadataValue(CitizensAPI.getPlugin(), true)); getEntity().setMetadata("NPC", new FixedMetadataValue(CitizensAPI.getPlugin(), true));
@ -324,7 +310,6 @@ public class CitizensNPC extends AbstractNPC {
if (getEntity() instanceof SkinnableEntity && !hasTrait(SkinLayers.class)) { if (getEntity() instanceof SkinnableEntity && !hasTrait(SkinLayers.class)) {
((SkinnableEntity) getEntity()).setSkinFlags(EnumSet.allOf(SkinLayers.Layer.class)); ((SkinnableEntity) getEntity()).setSkinFlags(EnumSet.allOf(SkinLayers.Layer.class));
} }
Collection<Trait> onPreSpawn = traits.values(); Collection<Trait> onPreSpawn = traits.values();
for (Trait trait : onPreSpawn.toArray(new Trait[onPreSpawn.size()])) { for (Trait trait : onPreSpawn.toArray(new Trait[onPreSpawn.size()])) {
try { try {
@ -334,7 +319,6 @@ public class CitizensNPC extends AbstractNPC {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
boolean loaded = Messaging.isDebugging() ? false : Util.isLoaded(at); boolean loaded = Messaging.isDebugging() ? false : Util.isLoaded(at);
boolean couldSpawn = entityController.spawn(at); boolean couldSpawn = entityController.spawn(at);
@ -343,30 +327,27 @@ public class CitizensNPC extends AbstractNPC {
Messaging.debug("Retrying spawn of", this, "later, SpawnReason." + reason + ". Was loaded", loaded, Messaging.debug("Retrying spawn of", this, "later, SpawnReason." + reason + ". Was loaded", loaded,
"is loaded", Util.isLoaded(at)); "is loaded", Util.isLoaded(at));
} }
// we need to wait before trying to spawn // we need to wait before trying to spawn
entityController.remove(); entityController.remove();
Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(this, at)); Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(this, at));
return false; return false;
} }
// send skin packets, if applicable, before other NMS packets are sent // send skin packets, if applicable, before other NMS packets are sent
SkinnableEntity skinnable = getEntity() instanceof SkinnableEntity ? ((SkinnableEntity) getEntity()) : null; SkinnableEntity skinnable = getEntity() instanceof SkinnableEntity ? (SkinnableEntity) getEntity() : null;
if (skinnable != null) { if (skinnable != null) {
skinnable.getSkinTracker().onSpawnNPC(); skinnable.getSkinTracker().onSpawnNPC();
} }
NMS.setLocationDirectly(getEntity(), at); NMS.setLocationDirectly(getEntity(), at);
NMS.setHeadYaw(getEntity(), at.getYaw()); NMS.setHeadYaw(getEntity(), at.getYaw());
NMS.setBodyYaw(getEntity(), at.getYaw()); NMS.setBodyYaw(getEntity(), at.getYaw());
final Location to = at; Location to = at;
Consumer<Runnable> postSpawn = new Consumer<Runnable>() { Consumer<Runnable> postSpawn = new Consumer<Runnable>() {
private int timer; private int timer;
@Override @Override
public void accept(Runnable cancel) { public void accept(Runnable cancel) {
if (getEntity() == null || (!hasTrait(PacketNPC.class) && !getEntity().isValid())) { if (getEntity() == null || !hasTrait(PacketNPC.class) && !getEntity().isValid()) {
if (timer++ > Setting.ENTITY_SPAWN_WAIT_DURATION.asTicks()) { if (timer++ > Setting.ENTITY_SPAWN_WAIT_DURATION.asTicks()) {
Messaging.debug("Couldn't spawn ", CitizensNPC.this, "waited", timer, Messaging.debug("Couldn't spawn ", CitizensNPC.this, "waited", timer,
"ticks but entity not added to world"); "ticks but entity not added to world");
@ -374,10 +355,8 @@ public class CitizensNPC extends AbstractNPC {
cancel.run(); cancel.run();
Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(CitizensNPC.this, to)); Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(CitizensNPC.this, to));
} }
return; return;
} }
// Set the spawned state // Set the spawned state
getOrAddTrait(CurrentLocation.class).setLocation(to); getOrAddTrait(CurrentLocation.class).setLocation(to);
getOrAddTrait(Spawned.class).setSpawned(true); getOrAddTrait(Spawned.class).setSpawned(true);
@ -392,7 +371,6 @@ public class CitizensNPC extends AbstractNPC {
cancel.run(); cancel.run();
return; return;
} }
navigator.onSpawn(); navigator.onSpawn();
for (Trait trait : Iterables.toArray(traits.values(), Trait.class)) { for (Trait trait : Iterables.toArray(traits.values(), Trait.class)) {
@ -403,7 +381,6 @@ public class CitizensNPC extends AbstractNPC {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
EntityType type = getEntity().getType(); EntityType type = getEntity().getType();
NMS.replaceTracker(getEntity()); NMS.replaceTracker(getEntity());
@ -414,13 +391,11 @@ public class CitizensNPC extends AbstractNPC {
if (NMS.getStepHeight(entity) < 1) { if (NMS.getStepHeight(entity) < 1) {
NMS.setStepHeight(entity, 1); NMS.setStepHeight(entity, 1);
} }
if (type == EntityType.PLAYER) { if (type == EntityType.PLAYER) {
PlayerUpdateTask.registerPlayer(getEntity()); PlayerUpdateTask.registerPlayer(getEntity());
} else if (data().has(NPC.Metadata.AGGRESSIVE)) { } else if (data().has(NPC.Metadata.AGGRESSIVE)) {
NMS.setAggressive(entity, data().<Boolean> get(NPC.Metadata.AGGRESSIVE)); NMS.setAggressive(entity, data().<Boolean> get(NPC.Metadata.AGGRESSIVE));
} }
if (SUPPORT_NODAMAGE_TICKS && (Setting.DEFAULT_SPAWN_NODAMAGE_DURATION.asTicks() != 20 if (SUPPORT_NODAMAGE_TICKS && (Setting.DEFAULT_SPAWN_NODAMAGE_DURATION.asTicks() != 20
|| data().has(NPC.Metadata.SPAWN_NODAMAGE_TICKS))) { || data().has(NPC.Metadata.SPAWN_NODAMAGE_TICKS))) {
try { try {
@ -431,11 +406,9 @@ public class CitizensNPC extends AbstractNPC {
} }
} }
} }
if (requiresNameHologram() && !hasTrait(HologramTrait.class)) { if (requiresNameHologram() && !hasTrait(HologramTrait.class)) {
addTrait(HologramTrait.class); addTrait(HologramTrait.class);
} }
updateFlyableState(); updateFlyableState();
updateCustomNameVisibility(); updateCustomNameVisibility();
updateScoreboard(); updateScoreboard();
@ -451,11 +424,10 @@ public class CitizensNPC extends AbstractNPC {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
postSpawn.accept(() -> cancel()); postSpawn.accept(this::cancel);
} }
}.runTaskTimer(CitizensAPI.getPlugin(), 0, 1); }.runTaskTimer(CitizensAPI.getPlugin(), 0, 1);
} }
return true; return true;
} }
@ -467,13 +439,11 @@ public class CitizensNPC extends AbstractNPC {
if (hasTrait(SitTrait.class) && getOrAddTrait(SitTrait.class).isSitting()) { if (hasTrait(SitTrait.class) && getOrAddTrait(SitTrait.class).isSitting()) {
getOrAddTrait(SitTrait.class).setSitting(location); getOrAddTrait(SitTrait.class).setSitting(location);
} }
Location npcLoc = getEntity().getLocation(); Location npcLoc = getEntity().getLocation();
if (isSpawned() && npcLoc.getWorld() == location.getWorld()) { if (isSpawned() && npcLoc.getWorld() == location.getWorld()) {
if (npcLoc.distance(location) < 1) { if (npcLoc.distance(location) < 1) {
NMS.setHeadYaw(getEntity(), location.getYaw()); NMS.setHeadYaw(getEntity(), location.getYaw());
} }
if (getEntity().getType() == EntityType.PLAYER && !getEntity().isInsideVehicle() if (getEntity().getType() == EntityType.PLAYER && !getEntity().isInsideVehicle()
&& NMS.getPassengers(getEntity()).size() == 0) { && NMS.getPassengers(getEntity()).size() == 0) {
NPCTeleportEvent event = new NPCTeleportEvent(this, location); NPCTeleportEvent event = new NPCTeleportEvent(this, location);
@ -484,7 +454,6 @@ public class CitizensNPC extends AbstractNPC {
return; return;
} }
} }
super.teleport(location, reason); super.teleport(location, reason);
} }
@ -502,7 +471,6 @@ public class CitizensNPC extends AbstractNPC {
resetCachedCoord(); resetCachedCoord();
return; return;
} }
if (data().has(NPC.Metadata.ACTIVATION_RANGE)) { if (data().has(NPC.Metadata.ACTIVATION_RANGE)) {
int range = data().get(NPC.Metadata.ACTIVATION_RANGE); int range = data().get(NPC.Metadata.ACTIVATION_RANGE);
if (range == -1 || CitizensAPI.getLocationLookup().getNearbyPlayers(getStoredLocation(), range) if (range == -1 || CitizensAPI.getLocationLookup().getNearbyPlayers(getStoredLocation(), range)
@ -510,7 +478,6 @@ public class CitizensNPC extends AbstractNPC {
NMS.activate(getEntity()); NMS.activate(getEntity());
} }
} }
boolean shouldSwim = data().get(NPC.Metadata.SWIMMING, SwimmingExaminer.isWaterMob(getEntity())) boolean shouldSwim = data().get(NPC.Metadata.SWIMMING, SwimmingExaminer.isWaterMob(getEntity()))
&& MinecraftBlockExaminer.isLiquid(getEntity().getLocation().getBlock().getType()); && MinecraftBlockExaminer.isLiquid(getEntity().getLocation().getBlock().getType());
if (navigator.isNavigating()) { if (navigator.isNavigating()) {
@ -528,7 +495,6 @@ public class CitizensNPC extends AbstractNPC {
NMS.trySwim(getEntity()); NMS.trySwim(getEntity());
} }
} }
if (SUPPORT_GLOWING && data().has(NPC.Metadata.GLOWING)) { if (SUPPORT_GLOWING && data().has(NPC.Metadata.GLOWING)) {
try { try {
getEntity().setGlowing(data().get(NPC.Metadata.GLOWING, false)); getEntity().setGlowing(data().get(NPC.Metadata.GLOWING, false));
@ -536,7 +502,6 @@ public class CitizensNPC extends AbstractNPC {
SUPPORT_GLOWING = false; SUPPORT_GLOWING = false;
} }
} }
if (SUPPORT_SILENT && data().has(NPC.Metadata.SILENT)) { if (SUPPORT_SILENT && data().has(NPC.Metadata.SILENT)) {
try { try {
getEntity().setSilent(Boolean.parseBoolean(data().get(NPC.Metadata.SILENT).toString())); getEntity().setSilent(Boolean.parseBoolean(data().get(NPC.Metadata.SILENT).toString()));
@ -544,7 +509,6 @@ public class CitizensNPC extends AbstractNPC {
SUPPORT_SILENT = false; SUPPORT_SILENT = false;
} }
} }
boolean isLiving = getEntity() instanceof LivingEntity; boolean isLiving = getEntity() instanceof LivingEntity;
if (isUpdating(NPCUpdate.PACKET)) { if (isUpdating(NPCUpdate.PACKET)) {
if (data().get(NPC.Metadata.KEEP_CHUNK_LOADED, Setting.KEEP_CHUNKS_LOADED.asBoolean())) { if (data().get(NPC.Metadata.KEEP_CHUNK_LOADED, Setting.KEEP_CHUNKS_LOADED.asBoolean())) {
@ -572,7 +536,6 @@ public class CitizensNPC extends AbstractNPC {
SUPPORT_PICKUP_ITEMS = false; SUPPORT_PICKUP_ITEMS = false;
} }
} }
if (getEntity() instanceof Player) { if (getEntity() instanceof Player) {
updateUsingItemState((Player) getEntity()); updateUsingItemState((Player) getEntity());
if (data().has(NPC.Metadata.SNEAKING) && !hasTrait(SneakTrait.class)) { if (data().has(NPC.Metadata.SNEAKING) && !hasTrait(SneakTrait.class)) {
@ -580,7 +543,6 @@ public class CitizensNPC extends AbstractNPC {
} }
} }
} }
navigator.run(); navigator.run();
updateCounter++; updateCounter++;
@ -616,16 +578,12 @@ public class CitizensNPC extends AbstractNPC {
return; return;
EntityType type = isSpawned() ? getEntity().getType() : getOrAddTrait(MobType.class).getType(); EntityType type = isSpawned() ? getEntity().getType() : getOrAddTrait(MobType.class).getType();
if (type == null) if (type == null || !Util.isAlwaysFlyable(type))
return;
if (!Util.isAlwaysFlyable(type))
return; return;
if (!data().has(NPC.Metadata.FLYABLE)) { if (!data().has(NPC.Metadata.FLYABLE)) {
data().setPersistent(NPC.Metadata.FLYABLE, true); data().setPersistent(NPC.Metadata.FLYABLE, true);
} }
if (!hasTrait(Gravity.class)) { if (!hasTrait(Gravity.class)) {
getOrAddTrait(Gravity.class).setEnabled(true); getOrAddTrait(Gravity.class).setEnabled(true);
} }
@ -657,7 +615,7 @@ public class CitizensNPC extends AbstractNPC {
} }
} }
private static final SetMultimap<ChunkCoord, NPC> CHUNK_LOADERS = HashMultimap.create(); private static SetMultimap<ChunkCoord, NPC> CHUNK_LOADERS = HashMultimap.create();
private static boolean SUPPORT_GLOWING = true; private static boolean SUPPORT_GLOWING = true;
private static boolean SUPPORT_NODAMAGE_TICKS = true; private static boolean SUPPORT_NODAMAGE_TICKS = true;
private static boolean SUPPORT_PICKUP_ITEMS = true; private static boolean SUPPORT_PICKUP_ITEMS = true;

View File

@ -2,6 +2,7 @@ package net.citizensnpcs.npc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -37,7 +38,7 @@ import net.citizensnpcs.util.NMS;
public class CitizensNPCRegistry implements NPCRegistry { public class CitizensNPCRegistry implements NPCRegistry {
private final String name; private final String name;
private final TIntObjectHashMap<NPC> npcs = new TIntObjectHashMap<NPC>(); private final TIntObjectHashMap<NPC> npcs = new TIntObjectHashMap<>();
private final NPCDataStore saves; private final NPCDataStore saves;
private final Map<UUID, NPC> uniqueNPCs = Maps.newHashMap(); private final Map<UUID, NPC> uniqueNPCs = Maps.newHashMap();
@ -74,11 +75,9 @@ public class CitizensNPCRegistry implements NPCRegistry {
if (type == EntityType.ARMOR_STAND && !npc.hasTrait(ArmorStandTrait.class)) { if (type == EntityType.ARMOR_STAND && !npc.hasTrait(ArmorStandTrait.class)) {
npc.addTrait(ArmorStandTrait.class); npc.addTrait(ArmorStandTrait.class);
} }
if (Setting.DEFAULT_LOOK_CLOSE.asBoolean()) { if (Setting.DEFAULT_LOOK_CLOSE.asBoolean()) {
npc.addTrait(LookClose.class); npc.addTrait(LookClose.class);
} }
npc.addTrait(MountTrait.class); npc.addTrait(MountTrait.class);
return npc; return npc;
} }
@ -93,9 +92,8 @@ public class CitizensNPCRegistry implements NPCRegistry {
npc.data().set(NPC.Metadata.ITEM_ID, item.getType().name()); npc.data().set(NPC.Metadata.ITEM_ID, item.getType().name());
npc.data().set(NPC.Metadata.ITEM_DATA, item.getData().getData()); npc.data().set(NPC.Metadata.ITEM_DATA, item.getData().getData());
npc.setItemProvider(() -> item); npc.setItemProvider(() -> item);
} else { } else
throw new UnsupportedOperationException("Not an item entity type"); throw new UnsupportedOperationException("Not an item entity type");
}
return npc; return npc;
} }
@ -174,14 +172,13 @@ public class CitizensNPCRegistry implements NPCRegistry {
if (npc != null) if (npc != null)
return npc; return npc;
for (NPCRegistry registry : CitizensAPI.getNPCRegistries()) { for (NPCRegistry registry : CitizensAPI.getNPCRegistries()) {
if (registry == this) if (registry == this) {
continue; continue;
NPC other = registry.getByUniqueId(uuid);
if (other != null) {
return other;
} }
NPC other = registry.getByUniqueId(uuid);
if (other != null)
return other;
} }
return null; return null;
} }
@ -243,8 +240,8 @@ public class CitizensNPCRegistry implements NPCRegistry {
@Override @Override
public Iterable<NPC> sorted() { public Iterable<NPC> sorted() {
List<NPC> vals = new ArrayList<NPC>(npcs.valueCollection()); List<NPC> vals = new ArrayList<>(npcs.valueCollection());
Collections.sort(vals, (a, b) -> Integer.compare(a.getId(), b.getId())); Collections.sort(vals, Comparator.comparing(NPC::getId));
return vals; return vals;
} }
} }

View File

@ -157,9 +157,8 @@ public class CitizensTraitFactory implements TraitFactory {
@Override @Override
public <T extends Trait> T getTrait(Class<T> clazz) { public <T extends Trait> T getTrait(Class<T> clazz) {
for (TraitInfo entry : registered.values()) { for (TraitInfo entry : registered.values()) {
if (clazz == entry.getTraitClass()) { if (clazz == entry.getTraitClass())
return create(entry); return create(entry);
}
} }
return null; return null;
} }
@ -183,9 +182,8 @@ public class CitizensTraitFactory implements TraitFactory {
public void registerTrait(TraitInfo info) { public void registerTrait(TraitInfo info) {
Preconditions.checkNotNull(info, "info cannot be null"); Preconditions.checkNotNull(info, "info cannot be null");
info.checkValid(); info.checkValid();
if (registered.containsKey(info.getTraitName())) { if (registered.containsKey(info.getTraitName()))
throw new IllegalArgumentException("Trait name " + info.getTraitName() + " already registered"); throw new IllegalArgumentException("Trait name " + info.getTraitName() + " already registered");
}
registered.put(info.getTraitName(), info); registered.put(info.getTraitName(), info);
if (info.isDefaultTrait()) { if (info.isDefaultTrait()) {
defaultTraits.add(info); defaultTraits.add(info);

View File

@ -37,6 +37,5 @@ public class EntityControllers {
} }
} }
private static final Map<EntityType, Constructor<? extends EntityController>> TYPES = Maps private static Map<EntityType, Constructor<? extends EntityController>> TYPES = Maps.newEnumMap(EntityType.class);
.newEnumMap(EntityType.class);
} }

View File

@ -57,11 +57,11 @@ public class NPCSelector implements Listener, net.citizensnpcs.api.npc.NPCSelect
if (event.getSelected() != null) if (event.getSelected() != null)
return event.getSelected(); return event.getSelected();
if (sender instanceof Player) { if (sender instanceof Player)
return getSelectedFromMetadatable((Player) sender); return getSelectedFromMetadatable((Player) sender);
} else if (sender instanceof BlockCommandSender) { else if (sender instanceof BlockCommandSender)
return getSelectedFromMetadatable(((BlockCommandSender) sender).getBlock()); return getSelectedFromMetadatable(((BlockCommandSender) sender).getBlock());
} else if (sender instanceof ConsoleCommandSender) { else if (sender instanceof ConsoleCommandSender) {
if (consoleSelectedNPC == null) if (consoleSelectedNPC == null)
return null; return null;
return CitizensAPI.getNPCRegistry().getByUniqueIdGlobal(consoleSelectedNPC); return CitizensAPI.getNPCRegistry().getByUniqueIdGlobal(consoleSelectedNPC);
@ -90,7 +90,7 @@ public class NPCSelector implements Listener, net.citizensnpcs.api.npc.NPCSelect
if (value.equals("console")) { if (value.equals("console")) {
consoleSelectedNPC = null; consoleSelectedNPC = null;
} else if (value.startsWith("@")) { } else if (value.startsWith("@")) {
String[] parts = value.substring(1, value.length()).split(":"); String[] parts = value.substring(1).split(":");
World world = Bukkit.getWorld(parts[0]); World world = Bukkit.getWorld(parts[0]);
if (world != null) { if (world != null) {
Block block = world.getBlockAt(Integer.parseInt(parts[1]), Integer.parseInt(parts[2]), Block block = world.getBlockAt(Integer.parseInt(parts[1]), Integer.parseInt(parts[2]),
@ -159,7 +159,6 @@ public class NPCSelector implements Listener, net.citizensnpcs.api.npc.NPCSelect
consoleSelectedNPC = npc.getUniqueId(); consoleSelectedNPC = npc.getUniqueId();
selectors.add("console"); selectors.add("console");
} }
Bukkit.getPluginManager().callEvent(new NPCSelectEvent(npc, sender)); Bukkit.getPluginManager().callEvent(new NPCSelectEvent(npc, sender));
} }

View File

@ -50,9 +50,10 @@ public class Template {
queue.add(new Node(fullKey, (Map<String, Object>) entry.getValue())); queue.add(new Node(fullKey, (Map<String, Object>) entry.getValue()));
continue; continue;
} }
boolean overwrite = memoryKey.keyExists(fullKey) | override; boolean overwrite = memoryKey.keyExists(fullKey) || override;
if (!overwrite || fullKey.equals("uuid")) if (!overwrite || fullKey.equals("uuid")) {
continue; continue;
}
memoryKey.setRaw(fullKey, entry.getValue()); memoryKey.setRaw(fullKey, entry.getValue());
} }
} }

View File

@ -40,15 +40,15 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
super(TargetType.LOCATION); super(TargetType.LOCATION);
List<Vector> list = Lists.newArrayList(path); List<Vector> list = Lists.newArrayList(path);
this.params = params; this.params = params;
this.destination = list.get(list.size() - 1).toLocation(npc.getStoredLocation().getWorld()); destination = list.get(list.size() - 1).toLocation(npc.getStoredLocation().getWorld());
this.npc = npc; this.npc = npc;
this.plan = new Path(list); plan = new Path(list);
} }
public AStarNavigationStrategy(NPC npc, Location dest, NavigatorParameters params) { public AStarNavigationStrategy(NPC npc, Location dest, NavigatorParameters params) {
super(TargetType.LOCATION); super(TargetType.LOCATION);
this.params = params; this.params = params;
this.destination = dest; destination = dest;
this.npc = npc; this.npc = npc;
} }
@ -91,9 +91,8 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
planner = null; planner = null;
} }
} }
if (getCancelReason() != null || plan == null || plan.isComplete()) { if (getCancelReason() != null || plan == null || plan.isComplete())
return true; return true;
}
if (vector == null) { if (vector == null) {
vector = plan.getCurrentVector(); vector = plan.getCurrentVector();
} }
@ -116,23 +115,20 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
double xzDistance = dX * dX + dZ * dZ; double xzDistance = dX * dX + dZ * dZ;
if (Math.abs(dY) < 1 && Math.sqrt(xzDistance) <= params.distanceMargin()) { if (Math.abs(dY) < 1 && Math.sqrt(xzDistance) <= params.distanceMargin()) {
plan.update(npc); plan.update(npc);
if (plan.isComplete()) { if (plan.isComplete())
return true; return true;
}
vector = plan.getCurrentVector(); vector = plan.getCurrentVector();
return false; return false;
} }
if (params.debug()) { if (params.debug()) {
npc.getEntity().getWorld().playEffect(dest, Effect.ENDER_SIGNAL, 0); npc.getEntity().getWorld().playEffect(dest, Effect.ENDER_SIGNAL, 0);
} }
if (npc.getEntity() instanceof LivingEntity && !npc.getEntity().getType().name().contains("ARMOR_STAND")) { if (npc.getEntity() instanceof LivingEntity && !npc.getEntity().getType().name().contains("ARMOR_STAND")) {
NMS.setDestination(npc.getEntity(), dest.getX(), dest.getY(), dest.getZ(), params.speed()); NMS.setDestination(npc.getEntity(), dest.getX(), dest.getY(), dest.getZ(), params.speed());
} else { } else {
Vector dir = dest.toVector().subtract(npc.getEntity().getLocation().toVector()).normalize().multiply(0.2); Vector dir = dest.toVector().subtract(npc.getEntity().getLocation().toVector()).normalize().multiply(0.2);
boolean liquidOrInLiquid = MinecraftBlockExaminer.isLiquidOrInLiquid(loc.getBlock()); boolean liquidOrInLiquid = MinecraftBlockExaminer.isLiquidOrInLiquid(loc.getBlock());
if ((dY >= 1 && Math.sqrt(xzDistance) <= 0.4) || (dY >= 0.2 && liquidOrInLiquid)) { if (dY >= 1 && Math.sqrt(xzDistance) <= 0.4 || dY >= 0.2 && liquidOrInLiquid) {
dir.add(new Vector(0, 0.75, 0)); dir.add(new Vector(0, 0.75, 0));
} }
npc.getEntity().setVelocity(dir); npc.getEntity().setVelocity(dir);
@ -176,18 +172,16 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
} }
public CancelReason tick(int iterationsPerTick, int maxIterations) { public CancelReason tick(int iterationsPerTick, int maxIterations) {
if (this.plan != null) if (plan != null)
return null; return null;
Path plan = ASTAR.run(state, iterationsPerTick); Path plan = ASTAR.run(state, iterationsPerTick);
if (plan == null) { if (plan == null) {
if (state.isEmpty()) { if (state.isEmpty())
return CancelReason.STUCK; return CancelReason.STUCK;
}
if (iterationsPerTick > 0 && maxIterations > 0) { if (iterationsPerTick > 0 && maxIterations > 0) {
iterations += iterationsPerTick; iterations += iterationsPerTick;
if (iterations > maxIterations) { if (iterations > maxIterations)
return CancelReason.STUCK; return CancelReason.STUCK;
}
} }
} else { } else {
this.plan = plan; this.plan = plan;
@ -200,5 +194,5 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
} }
} }
private static final AStarMachine<VectorNode, Path> ASTAR = AStarMachine.createWithDefaultStorage(); private static AStarMachine<VectorNode, Path> ASTAR = AStarMachine.createWithDefaultStorage();
} }

View File

@ -18,8 +18,8 @@ public class BoundingBoxExaminer implements BlockExaminer {
public BoundingBoxExaminer(Entity entity) { public BoundingBoxExaminer(Entity entity) {
if (entity != null) { if (entity != null) {
this.height = NMS.getHeight(entity); height = NMS.getHeight(entity);
this.width = NMS.getWidth(entity); width = NMS.getWidth(entity);
} }
} }

View File

@ -73,7 +73,6 @@ public class CitizensNavigator implements Navigator, Runnable {
!Setting.DEFAULT_STUCK_ACTION.asString().contains("teleport"))) { !Setting.DEFAULT_STUCK_ACTION.asString().contains("teleport"))) {
defaultParams.stuckAction(null); defaultParams.stuckAction(null);
} }
defaultParams.examiner(new SwimmingExaminer(npc)); defaultParams.examiner(new SwimmingExaminer(npc));
} }
@ -116,9 +115,8 @@ public class CitizensNavigator implements Navigator, Runnable {
@Override @Override
public NavigatorParameters getLocalParameters() { public NavigatorParameters getLocalParameters() {
if (!isNavigating()) { if (!isNavigating())
return defaultParams; return defaultParams;
}
return localParams; return localParams;
} }
@ -156,23 +154,18 @@ public class CitizensNavigator implements Navigator, Runnable {
if (root.keyExists("pathfindingrange")) { if (root.keyExists("pathfindingrange")) {
defaultParams.range((float) root.getDouble("pathfindingrange")); defaultParams.range((float) root.getDouble("pathfindingrange"));
} }
if (root.keyExists("stationaryticks")) { if (root.keyExists("stationaryticks")) {
defaultParams.stationaryTicks(root.getInt("stationaryticks")); defaultParams.stationaryTicks(root.getInt("stationaryticks"));
} }
if (root.keyExists("distancemargin")) { if (root.keyExists("distancemargin")) {
defaultParams.distanceMargin(root.getDouble("distancemargin")); defaultParams.distanceMargin(root.getDouble("distancemargin"));
} }
if (root.keyExists("destinationteleportmargin")) { if (root.keyExists("destinationteleportmargin")) {
defaultParams.destinationTeleportMargin(root.getDouble("destinationteleportmargin")); defaultParams.destinationTeleportMargin(root.getDouble("destinationteleportmargin"));
} }
if (root.keyExists("updatepathrate")) { if (root.keyExists("updatepathrate")) {
defaultParams.updatePathRate(root.getInt("updatepathrate")); defaultParams.updatePathRate(root.getInt("updatepathrate"));
} }
defaultParams.speedModifier((float) root.getDouble("speedmodifier", 1F)); defaultParams.speedModifier((float) root.getDouble("speedmodifier", 1F));
defaultParams.avoidWater(root.getBoolean("avoidwater")); defaultParams.avoidWater(root.getBoolean("avoidwater"));
if (!root.getBoolean("usedefaultstuckaction") && defaultParams.stuckAction() == TeleportStuckAction.INSTANCE) { if (!root.getBoolean("usedefaultstuckaction") && defaultParams.stuckAction() == TeleportStuckAction.INSTANCE) {
@ -188,7 +181,6 @@ public class CitizensNavigator implements Navigator, Runnable {
if (defaultParams.baseSpeed() == UNINITIALISED_SPEED) { if (defaultParams.baseSpeed() == UNINITIALISED_SPEED) {
defaultParams.baseSpeed(NMS.getSpeedFor(npc)); defaultParams.baseSpeed(NMS.getSpeedFor(npc));
} }
updatePathfindingRange(); updatePathfindingRange();
} }
@ -205,7 +197,6 @@ public class CitizensNavigator implements Navigator, Runnable {
stopNavigating(CancelReason.STUCK); stopNavigating(CancelReason.STUCK);
return; return;
} }
if (updateStationaryStatus()) if (updateStationaryStatus())
return; return;
@ -214,7 +205,6 @@ public class CitizensNavigator implements Navigator, Runnable {
if (!finished) { if (!finished) {
localParams.run(); localParams.run();
} }
if (localParams.lookAtFunction() != null) { if (localParams.lookAtFunction() != null) {
if (session == null) { if (session == null) {
RotationTrait trait = npc.getOrAddTrait(RotationTrait.class); RotationTrait trait = npc.getOrAddTrait(RotationTrait.class);
@ -223,14 +213,12 @@ public class CitizensNavigator implements Navigator, Runnable {
} }
session.getSession().rotateToFace(localParams.lookAtFunction().apply(this)); session.getSession().rotateToFace(localParams.lookAtFunction().apply(this));
} }
if (localParams.destinationTeleportMargin() > 0 if (localParams.destinationTeleportMargin() > 0
&& npcLoc.distance(targetLoc) <= localParams.destinationTeleportMargin()) { && npcLoc.distance(targetLoc) <= localParams.destinationTeleportMargin()) {
// TODO: easing? // TODO: easing?
npc.teleport(targetLoc, TeleportCause.PLUGIN); npc.teleport(targetLoc, TeleportCause.PLUGIN);
finished = true; finished = true;
} }
if (!finished) if (!finished)
return; return;
@ -252,37 +240,31 @@ public class CitizensNavigator implements Navigator, Runnable {
} else { } else {
root.removeKey("pathfindingrange"); root.removeKey("pathfindingrange");
} }
if (defaultParams.stationaryTicks() != Setting.DEFAULT_STATIONARY_DURATION.asTicks()) { if (defaultParams.stationaryTicks() != Setting.DEFAULT_STATIONARY_DURATION.asTicks()) {
root.setInt("stationaryticks", defaultParams.stationaryTicks()); root.setInt("stationaryticks", defaultParams.stationaryTicks());
} else { } else {
root.removeKey("stationaryticks"); root.removeKey("stationaryticks");
} }
if (defaultParams.destinationTeleportMargin() != Setting.DEFAULT_DESTINATION_TELEPORT_MARGIN.asDouble()) { if (defaultParams.destinationTeleportMargin() != Setting.DEFAULT_DESTINATION_TELEPORT_MARGIN.asDouble()) {
root.setDouble("destinationteleportmargin", defaultParams.destinationTeleportMargin()); root.setDouble("destinationteleportmargin", defaultParams.destinationTeleportMargin());
} else { } else {
root.removeKey("destinationteleportmargin"); root.removeKey("destinationteleportmargin");
} }
if (defaultParams.distanceMargin() != Setting.DEFAULT_DISTANCE_MARGIN.asDouble()) { if (defaultParams.distanceMargin() != Setting.DEFAULT_DISTANCE_MARGIN.asDouble()) {
root.setDouble("distancemargin", defaultParams.distanceMargin()); root.setDouble("distancemargin", defaultParams.distanceMargin());
} else { } else {
root.removeKey("distancemargin"); root.removeKey("distancemargin");
} }
if (defaultParams.updatePathRate() != Setting.DEFAULT_PATHFINDER_UPDATE_PATH_RATE.asTicks()) { if (defaultParams.updatePathRate() != Setting.DEFAULT_PATHFINDER_UPDATE_PATH_RATE.asTicks()) {
root.setInt("updatepathrate", defaultParams.updatePathRate()); root.setInt("updatepathrate", defaultParams.updatePathRate());
} else { } else {
root.removeKey("updatepathrate"); root.removeKey("updatepathrate");
} }
if (defaultParams.useNewPathfinder() != Setting.USE_NEW_PATHFINDER.asBoolean()) { if (defaultParams.useNewPathfinder() != Setting.USE_NEW_PATHFINDER.asBoolean()) {
root.setBoolean("usenewpathfinder", defaultParams.useNewPathfinder()); root.setBoolean("usenewpathfinder", defaultParams.useNewPathfinder());
} else { } else {
root.removeKey("usenewpathfinder"); root.removeKey("usenewpathfinder");
} }
root.setDouble("speedmodifier", defaultParams.speedModifier()); root.setDouble("speedmodifier", defaultParams.speedModifier());
root.setBoolean("avoidwater", defaultParams.avoidWater()); root.setBoolean("avoidwater", defaultParams.avoidWater());
root.setBoolean("usedefaultstuckaction", defaultParams.stuckAction() == TeleportStuckAction.INSTANCE); root.setBoolean("usedefaultstuckaction", defaultParams.stuckAction() == TeleportStuckAction.INSTANCE);
@ -305,7 +287,6 @@ public class CitizensNavigator implements Navigator, Runnable {
cancelNavigation(); cancelNavigation();
return; return;
} }
setTarget(params -> { setTarget(params -> {
params.straightLineTargetingDistance(100000); params.straightLineTargetingDistance(100000);
return new MCTargetStrategy(npc, target, aggressive, params); return new MCTargetStrategy(npc, target, aggressive, params);
@ -321,7 +302,6 @@ public class CitizensNavigator implements Navigator, Runnable {
cancelNavigation(); cancelNavigation();
return; return;
} }
setTarget(params -> new StraightLineNavigationStrategy(npc, target.clone(), params)); setTarget(params -> new StraightLineNavigationStrategy(npc, target.clone(), params));
} }
@ -334,7 +314,6 @@ public class CitizensNavigator implements Navigator, Runnable {
cancelNavigation(); cancelNavigation();
return; return;
} }
setTarget(params -> new MCTargetStrategy(npc, target, aggressive, params)); setTarget(params -> new MCTargetStrategy(npc, target, aggressive, params));
} }
@ -354,15 +333,13 @@ public class CitizensNavigator implements Navigator, Runnable {
cancelNavigation(); cancelNavigation();
return; return;
} }
setTarget(params -> { setTarget(params -> {
if (npc.isFlyable()) { if (npc.isFlyable())
return new FlyingAStarNavigationStrategy(npc, path, params); return new FlyingAStarNavigationStrategy(npc, path, params);
} else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) { else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity))
return new AStarNavigationStrategy(npc, path, params); return new AStarNavigationStrategy(npc, path, params);
} else { else
return new MCNavigationStrategy(npc, path, params); return new MCNavigationStrategy(npc, path, params);
}
}); });
} }
@ -374,16 +351,14 @@ public class CitizensNavigator implements Navigator, Runnable {
cancelNavigation(); cancelNavigation();
return; return;
} }
Location target = targetIn.clone();
final Location target = targetIn.clone();
setTarget(params -> { setTarget(params -> {
if (npc.isFlyable()) { if (npc.isFlyable())
return new FlyingAStarNavigationStrategy(npc, target, params); return new FlyingAStarNavigationStrategy(npc, target, params);
} else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) { else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity))
return new AStarNavigationStrategy(npc, target, params); return new AStarNavigationStrategy(npc, target, params);
} else { else
return new MCNavigationStrategy(npc, target, params); return new MCNavigationStrategy(npc, target, params);
}
}); });
} }
@ -391,7 +366,6 @@ public class CitizensNavigator implements Navigator, Runnable {
if (executing != null) { if (executing != null) {
executing.stop(); executing.stop();
} }
executing = null; executing = null;
localParams = defaultParams; localParams = defaultParams;
@ -402,16 +376,11 @@ public class CitizensNavigator implements Navigator, Runnable {
npc.getEntity().setVelocity(velocity); npc.getEntity().setVelocity(velocity);
NMS.cancelMoveDestination(npc.getEntity()); NMS.cancelMoveDestination(npc.getEntity());
} }
if (!SUPPORT_CHUNK_TICKETS || !CitizensAPI.hasImplementation() || !CitizensAPI.getPlugin().isEnabled()) if (!SUPPORT_CHUNK_TICKETS || !CitizensAPI.hasImplementation() || !CitizensAPI.getPlugin().isEnabled())
return; return;
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() { Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(),
@Override () -> updateTicket(isNavigating() ? executing.getTargetAsLocation() : null), 10);
public void run() {
updateTicket(isNavigating() ? executing.getTargetAsLocation() : null);
}
}, 10);
// Location loc = npc.getEntity().getLocation(STATIONARY_LOCATION); // Location loc = npc.getEntity().getLocation(STATIONARY_LOCATION);
// NMS.look(npc.getEntity(), loc.getYaw(), 0); // NMS.look(npc.getEntity(), loc.getYaw(), 0);
@ -424,28 +393,23 @@ public class CitizensNavigator implements Navigator, Runnable {
if (reason == CancelReason.STUCK && Messaging.isDebugging()) { if (reason == CancelReason.STUCK && Messaging.isDebugging()) {
Messaging.debug(npc, "navigation ended, stuck", executing); Messaging.debug(npc, "navigation ended, stuck", executing);
} }
if (session != null) { if (session != null) {
session.end(); session.end();
session = null; session = null;
} }
Iterator<NavigatorCallback> itr = localParams.callbacks().iterator(); Iterator<NavigatorCallback> itr = localParams.callbacks().iterator();
List<NavigatorCallback> callbacks = new ArrayList<NavigatorCallback>(); List<NavigatorCallback> callbacks = new ArrayList<>();
while (itr.hasNext()) { while (itr.hasNext()) {
callbacks.add(itr.next()); callbacks.add(itr.next());
itr.remove(); itr.remove();
} }
for (NavigatorCallback callback : callbacks) { for (NavigatorCallback callback : callbacks) {
callback.onCompletion(reason); callback.onCompletion(reason);
} }
if (reason == null) { if (reason == null) {
stopNavigating(); stopNavigating();
return; return;
} }
if (reason == CancelReason.STUCK) { if (reason == CancelReason.STUCK) {
StuckAction action = localParams.stuckAction(); StuckAction action = localParams.stuckAction();
NavigationStuckEvent event = new NavigationStuckEvent(this, action); NavigationStuckEvent event = new NavigationStuckEvent(this, action);
@ -458,7 +422,6 @@ public class CitizensNavigator implements Navigator, Runnable {
return; return;
} }
} }
NavigationCancelEvent event = new NavigationCancelEvent(this, reason); NavigationCancelEvent event = new NavigationCancelEvent(this, reason);
PathStrategy old = executing; PathStrategy old = executing;
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
@ -534,7 +497,6 @@ public class CitizensNavigator implements Navigator, Runnable {
stopNavigating(CancelReason.STUCK); stopNavigating(CancelReason.STUCK);
return true; return true;
} }
if (lastX == current.getBlockX() && lastY == current.getBlockY() && lastZ == current.getBlockZ()) { if (lastX == current.getBlockX() && lastY == current.getBlockY() && lastZ == current.getBlockZ()) {
if (++stationaryTicks >= localParams.stationaryTicks()) { if (++stationaryTicks >= localParams.stationaryTicks()) {
stopNavigating(CancelReason.STUCK); stopNavigating(CancelReason.STUCK);
@ -543,7 +505,6 @@ public class CitizensNavigator implements Navigator, Runnable {
} else { } else {
stationaryTicks = 0; stationaryTicks = 0;
} }
lastX = current.getBlockX(); lastX = current.getBlockX();
lastY = current.getBlockY(); lastY = current.getBlockY();
lastZ = current.getBlockZ(); lastZ = current.getBlockZ();
@ -554,32 +515,29 @@ public class CitizensNavigator implements Navigator, Runnable {
if (!SUPPORT_CHUNK_TICKETS || !CitizensAPI.hasImplementation() || !CitizensAPI.getPlugin().isEnabled()) if (!SUPPORT_CHUNK_TICKETS || !CitizensAPI.hasImplementation() || !CitizensAPI.getPlugin().isEnabled())
return; return;
if (target != null && this.activeTicket != null if (target != null && activeTicket != null
&& new ChunkCoord(target.getChunk()).equals(new ChunkCoord(this.activeTicket.getChunk()))) { && new ChunkCoord(target.getChunk()).equals(new ChunkCoord(activeTicket.getChunk()))) {
this.activeTicket = target.clone(); activeTicket = target.clone();
return; return;
} }
if (activeTicket != null) {
if (this.activeTicket != null) {
try { try {
this.activeTicket.getChunk().removePluginChunkTicket(CitizensAPI.getPlugin()); activeTicket.getChunk().removePluginChunkTicket(CitizensAPI.getPlugin());
} catch (NoSuchMethodError e) { } catch (NoSuchMethodError e) {
SUPPORT_CHUNK_TICKETS = false; SUPPORT_CHUNK_TICKETS = false;
this.activeTicket = null; activeTicket = null;
} }
} }
if (target == null) { if (target == null) {
this.activeTicket = null; activeTicket = null;
return; return;
} }
activeTicket = target.clone();
this.activeTicket = target.clone();
try { try {
this.activeTicket.getChunk().addPluginChunkTicket(CitizensAPI.getPlugin()); activeTicket.getChunk().addPluginChunkTicket(CitizensAPI.getPlugin());
} catch (NoSuchMethodError e) { } catch (NoSuchMethodError e) {
SUPPORT_CHUNK_TICKETS = false; SUPPORT_CHUNK_TICKETS = false;
this.activeTicket = null; activeTicket = null;
} }
} }

View File

@ -43,16 +43,16 @@ public class FlyingAStarNavigationStrategy extends AbstractPathStrategy {
public FlyingAStarNavigationStrategy(NPC npc, Iterable<Vector> path, NavigatorParameters params) { public FlyingAStarNavigationStrategy(NPC npc, Iterable<Vector> path, NavigatorParameters params) {
super(TargetType.LOCATION); super(TargetType.LOCATION);
List<Vector> list = Lists.newArrayList(path); List<Vector> list = Lists.newArrayList(path);
this.target = list.get(list.size() - 1).toLocation(npc.getStoredLocation().getWorld()); target = list.get(list.size() - 1).toLocation(npc.getStoredLocation().getWorld());
this.parameters = params; parameters = params;
this.npc = npc; this.npc = npc;
setPlan(new Path(list)); setPlan(new Path(list));
} }
public FlyingAStarNavigationStrategy(final NPC npc, Location dest, NavigatorParameters params) { public FlyingAStarNavigationStrategy(NPC npc, Location dest, NavigatorParameters params) {
super(TargetType.LOCATION); super(TargetType.LOCATION);
this.target = dest; target = dest;
this.parameters = params; parameters = params;
this.npc = npc; this.npc = npc;
} }
@ -89,7 +89,7 @@ public class FlyingAStarNavigationStrategy extends AbstractPathStrategy {
} }
public void setPlan(Path path) { public void setPlan(Path path) {
this.plan = path; plan = path;
if (plan == null || plan.isComplete()) { if (plan == null || plan.isComplete()) {
setCancelReason(CancelReason.STUCK); setCancelReason(CancelReason.STUCK);
} else { } else {
@ -133,15 +133,13 @@ public class FlyingAStarNavigationStrategy extends AbstractPathStrategy {
setPlan(plan); setPlan(plan);
} }
} }
if (getCancelReason() != null || plan == null || plan.isComplete()) { if (getCancelReason() != null || plan == null || plan.isComplete())
return true; return true;
}
Location current = npc.getEntity().getLocation(); Location current = npc.getEntity().getLocation();
if (current.toVector().distance(vector) <= parameters.distanceMargin()) { if (current.toVector().distance(vector) <= parameters.distanceMargin()) {
plan.update(npc); plan.update(npc);
if (plan.isComplete()) { if (plan.isComplete())
return true; return true;
}
vector = plan.getCurrentVector(); vector = plan.getCurrentVector();
} }
if (parameters.debug()) { if (parameters.debug()) {
@ -159,7 +157,6 @@ public class FlyingAStarNavigationStrategy extends AbstractPathStrategy {
// 1.8 compatibility // 1.8 compatibility
} }
} }
Vector centeredDest = new Vector(vector.getX() + 0.5D, vector.getY() + 0.1D, vector.getZ() + 0.5D); Vector centeredDest = new Vector(vector.getX() + 0.5D, vector.getY() + 0.1D, vector.getZ() + 0.5D);
double d0 = centeredDest.getX() - current.getX(); double d0 = centeredDest.getX() - current.getX();
double d1 = centeredDest.getY() - current.getY(); double d1 = centeredDest.getY() - current.getY();
@ -178,10 +175,9 @@ public class FlyingAStarNavigationStrategy extends AbstractPathStrategy {
NMS.setVerticalMovement(npc.getEntity(), 0.5); NMS.setVerticalMovement(npc.getEntity(), 0.5);
Util.faceLocation(npc.getEntity(), centeredDest.toLocation(npc.getEntity().getWorld())); Util.faceLocation(npc.getEntity(), centeredDest.toLocation(npc.getEntity().getWorld()));
} }
plan.run(npc); plan.run(npc);
return false; return false;
} }
private static final AStarMachine<VectorNode, Path> ASTAR = AStarMachine.createWithDefaultStorage(); private static AStarMachine<VectorNode, Path> ASTAR = AStarMachine.createWithDefaultStorage();
} }

View File

@ -23,24 +23,24 @@ public class MCNavigationStrategy extends AbstractPathStrategy {
private final NavigatorParameters parameters; private final NavigatorParameters parameters;
private final Location target; private final Location target;
MCNavigationStrategy(final NPC npc, Iterable<Vector> path, NavigatorParameters params) { MCNavigationStrategy(NPC npc, Iterable<Vector> path, NavigatorParameters params) {
super(TargetType.LOCATION); super(TargetType.LOCATION);
List<Vector> list = Lists.newArrayList(path); List<Vector> list = Lists.newArrayList(path);
this.target = list.get(list.size() - 1).toLocation(npc.getStoredLocation().getWorld()); target = list.get(list.size() - 1).toLocation(npc.getStoredLocation().getWorld());
this.parameters = params; parameters = params;
entity = npc.getEntity(); entity = npc.getEntity();
this.navigator = NMS.getTargetNavigator(npc.getEntity(), list, params); navigator = NMS.getTargetNavigator(npc.getEntity(), list, params);
} }
MCNavigationStrategy(final NPC npc, Location dest, NavigatorParameters params) { MCNavigationStrategy(NPC npc, Location dest, NavigatorParameters params) {
super(TargetType.LOCATION); super(TargetType.LOCATION);
if (!MinecraftBlockExaminer.canStandIn(dest.getBlock())) { if (!MinecraftBlockExaminer.canStandIn(dest.getBlock())) {
dest = MinecraftBlockExaminer.findValidLocationAbove(dest, 2); dest = MinecraftBlockExaminer.findValidLocationAbove(dest, 2);
} }
this.target = Util.getCenterLocation(dest.getBlock()); target = Util.getCenterLocation(dest.getBlock());
this.parameters = params; parameters = params;
entity = npc.getEntity(); entity = npc.getEntity();
this.navigator = NMS.getTargetNavigator(entity, target, params); navigator = NMS.getTargetNavigator(entity, target, params);
} }
@Override @Override

View File

@ -31,17 +31,17 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
public MCTargetStrategy(NPC npc, org.bukkit.entity.Entity target, boolean aggro, NavigatorParameters params) { public MCTargetStrategy(NPC npc, org.bukkit.entity.Entity target, boolean aggro, NavigatorParameters params) {
this.npc = npc; this.npc = npc;
this.parameters = params; parameters = params;
this.handle = npc.getEntity(); handle = npc.getEntity();
this.target = target; this.target = target;
TargetNavigator nms = NMS.getTargetNavigator(npc.getEntity(), target, params); TargetNavigator nms = NMS.getTargetNavigator(npc.getEntity(), target, params);
this.targetNavigator = nms != null && !params.useNewPathfinder() ? nms : new AStarTargeter(); targetNavigator = nms != null && !params.useNewPathfinder() ? nms : new AStarTargeter();
this.aggro = aggro; this.aggro = aggro;
} }
private boolean canAttack() { private boolean canAttack() {
BoundingBox handleBB = NMS.getBoundingBox(handle), targetBB = NMS.getBoundingBox(target); BoundingBox handleBB = NMS.getBoundingBox(handle), targetBB = NMS.getBoundingBox(target);
return attackTicks <= 0 && (handleBB.maxY > targetBB.minY && handleBB.minY < targetBB.maxY) return attackTicks <= 0 && handleBB.maxY > targetBB.minY && handleBB.minY < targetBB.maxY
&& distance() <= parameters.attackRange() && ((LivingEntity) handle).hasLineOfSight(target); && distance() <= parameters.attackRange() && ((LivingEntity) handle).hasLineOfSight(target);
} }
@ -105,19 +105,16 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
cancelReason = CancelReason.TARGET_DIED; cancelReason = CancelReason.TARGET_DIED;
return true; return true;
} }
if (target.getWorld() != handle.getWorld()) { if (target.getWorld() != handle.getWorld()) {
cancelReason = CancelReason.TARGET_MOVED_WORLD; cancelReason = CancelReason.TARGET_MOVED_WORLD;
return true; return true;
} }
if (cancelReason != null) if (cancelReason != null)
return true; return true;
if (parameters.straightLineTargetingDistance() > 0 && !(targetNavigator instanceof StraightLineTargeter)) { if (parameters.straightLineTargetingDistance() > 0 && !(targetNavigator instanceof StraightLineTargeter)) {
targetNavigator = new StraightLineTargeter(targetNavigator); targetNavigator = new StraightLineTargeter(targetNavigator);
} }
if (!aggro && distance() <= parameters.distanceMargin()) { if (!aggro && distance() <= parameters.distanceMargin()) {
stop(); stop();
return false; return false;
@ -125,7 +122,6 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
targetNavigator.setPath(); targetNavigator.setPath();
updateCounter = 0; updateCounter = 0;
} }
targetNavigator.update(); targetNavigator.update();
NMS.look(handle, target); NMS.look(handle, target);
@ -137,11 +133,9 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
} }
attackTicks = parameters.attackDelayTicks(); attackTicks = parameters.attackDelayTicks();
} }
if (attackTicks > 0) { if (attackTicks > 0) {
attackTicks--; attackTicks--;
} }
return false; return false;
} }
@ -179,9 +173,8 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
private void setStrategy() { private void setStrategy() {
Location location = parameters.entityTargetLocationMapper().apply(target); Location location = parameters.entityTargetLocationMapper().apply(target);
if (location == null) { if (location == null)
throw new IllegalStateException("mapper should not return null"); throw new IllegalStateException("mapper should not return null");
}
if (!npc.isFlyable()) { if (!npc.isFlyable()) {
Block block = location.getBlock(); Block block = location.getBlock();
while (!MinecraftBlockExaminer.canStandOn(block.getRelative(BlockFace.DOWN))) { while (!MinecraftBlockExaminer.canStandOn(block.getRelative(BlockFace.DOWN))) {
@ -239,7 +232,6 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
active = new StraightLineNavigationStrategy(npc, target, parameters); active = new StraightLineNavigationStrategy(npc, target, parameters);
return; return;
} }
active = null; active = null;
fallback.setPath(); fallback.setPath();
} }

View File

@ -27,13 +27,13 @@ public class StraightLineNavigationStrategy extends AbstractPathStrategy {
this.params = params; this.params = params;
this.target = target; this.target = target;
this.npc = npc; this.npc = npc;
this.destination = params.entityTargetLocationMapper().apply(target); destination = params.entityTargetLocationMapper().apply(target);
} }
public StraightLineNavigationStrategy(NPC npc, Location dest, NavigatorParameters params) { public StraightLineNavigationStrategy(NPC npc, Location dest, NavigatorParameters params) {
super(TargetType.LOCATION); super(TargetType.LOCATION);
this.params = params; this.params = params;
this.destination = dest; destination = dest;
this.npc = npc; this.npc = npc;
} }
@ -68,7 +68,6 @@ public class StraightLineNavigationStrategy extends AbstractPathStrategy {
if (target != null) { if (target != null) {
destination = params.entityTargetLocationMapper().apply(target); destination = params.entityTargetLocationMapper().apply(target);
} }
Vector destVector = currLoc.toVector().add(destination.toVector().subtract(currLoc.toVector()).normalize()); Vector destVector = currLoc.toVector().add(destination.toVector().subtract(currLoc.toVector()).normalize());
Location destLoc = destVector.toLocation(destination.getWorld()); Location destLoc = destVector.toLocation(destination.getWorld());
if (!npc.isFlyable() && destVector.getBlockY() > currLoc.getBlockY()) { if (!npc.isFlyable() && destVector.getBlockY() > currLoc.getBlockY()) {
@ -84,7 +83,6 @@ public class StraightLineNavigationStrategy extends AbstractPathStrategy {
destLoc = block.getLocation(); destLoc = block.getLocation();
destVector = destLoc.toVector(); destVector = destLoc.toVector();
} }
double dX = destVector.getX() - currLoc.getX(); double dX = destVector.getX() - currLoc.getX();
double dZ = destVector.getZ() - currLoc.getZ(); double dZ = destVector.getZ() - currLoc.getZ();
double dY = destVector.getY() - currLoc.getY(); double dY = destVector.getY() - currLoc.getY();
@ -105,11 +103,9 @@ public class StraightLineNavigationStrategy extends AbstractPathStrategy {
while (normalisedTargetYaw >= 180.0F) { while (normalisedTargetYaw >= 180.0F) {
normalisedTargetYaw -= 360.0F; normalisedTargetYaw -= 360.0F;
} }
while (normalisedTargetYaw < -180.0F) { while (normalisedTargetYaw < -180.0F) {
normalisedTargetYaw += 360.0F; normalisedTargetYaw += 360.0F;
} }
if (npc.getEntity().getType() != EntityType.ENDER_DRAGON) { if (npc.getEntity().getType() != EntityType.ENDER_DRAGON) {
NMS.setVerticalMovement(npc.getEntity(), 0.5); NMS.setVerticalMovement(npc.getEntity(), 0.5);
NMS.setHeadYaw(npc.getEntity(), currLoc.getYaw() + normalisedTargetYaw); NMS.setHeadYaw(npc.getEntity(), currLoc.getYaw() + normalisedTargetYaw);
@ -121,7 +117,7 @@ public class StraightLineNavigationStrategy extends AbstractPathStrategy {
Vector dir = destVector.subtract(currLoc.toVector()).normalize().multiply(0.2); Vector dir = destVector.subtract(currLoc.toVector()).normalize().multiply(0.2);
Block in = currLoc.getBlock(); Block in = currLoc.getBlock();
if (distance > 0 && dY >= 1 && xzDistance <= 2.75 if (distance > 0 && dY >= 1 && xzDistance <= 2.75
|| (dY >= 0.2 && MinecraftBlockExaminer.isLiquidOrInLiquid(in))) { || dY >= 0.2 && MinecraftBlockExaminer.isLiquidOrInLiquid(in)) {
dir.add(new Vector(0, 0.75, 0)); dir.add(new Vector(0, 0.75, 0));
} }
Util.faceLocation(npc.getEntity(), destLoc); Util.faceLocation(npc.getEntity(), destLoc);

View File

@ -4,14 +4,6 @@ package net.citizensnpcs.npc.profile;
* The result status of a profile fetch. * The result status of a profile fetch.
*/ */
public enum ProfileFetchResult { public enum ProfileFetchResult {
/**
* The profile has not been fetched yet.
*/
PENDING,
/**
* The profile was successfully fetched.
*/
SUCCESS,
/** /**
* The profile request failed for unknown reasons. * The profile request failed for unknown reasons.
*/ */
@ -20,6 +12,14 @@ public enum ProfileFetchResult {
* The profile request failed because the profile was not found. * The profile request failed because the profile was not found.
*/ */
NOT_FOUND, NOT_FOUND,
/**
* The profile has not been fetched yet.
*/
PENDING,
/**
* The profile was successfully fetched.
*/
SUCCESS,
/** /**
* The profile request failed because too many requests were sent. * The profile request failed because too many requests were sent.
*/ */

View File

@ -31,8 +31,8 @@ import net.citizensnpcs.util.NMS;
* @see ProfileFetcher * @see ProfileFetcher
*/ */
class ProfileFetchThread implements Runnable { class ProfileFetchThread implements Runnable {
private final Deque<ProfileRequest> queue = new ArrayDeque<ProfileRequest>(); private final Deque<ProfileRequest> queue = new ArrayDeque<>();
private final Map<String, ProfileRequest> requested = new HashMap<String, ProfileRequest>(40); private final Map<String, ProfileRequest> requested = new HashMap<>(40);
private final Object sync = new Object(); // sync for queue & requested fields private final Object sync = new Object(); // sync for queue & requested fields
ProfileFetchThread() { ProfileFetchThread() {
@ -65,7 +65,6 @@ class ProfileFetchThread implements Runnable {
queue.add(request); queue.add(request);
} }
} }
if (handler != null) { if (handler != null) {
if (request.getResult() == ProfileFetchResult.PENDING if (request.getResult() == ProfileFetchResult.PENDING
|| request.getResult() == ProfileFetchResult.TOO_MANY_REQUESTS) { || request.getResult() == ProfileFetchResult.TOO_MANY_REQUESTS) {
@ -100,7 +99,6 @@ class ProfileFetchThread implements Runnable {
return; return;
} }
} }
if (handler != null) { if (handler != null) {
if (request.getResult() == ProfileFetchResult.PENDING if (request.getResult() == ProfileFetchResult.PENDING
|| request.getResult() == ProfileFetchResult.TOO_MANY_REQUESTS) { || request.getResult() == ProfileFetchResult.TOO_MANY_REQUESTS) {
@ -117,7 +115,7 @@ class ProfileFetchThread implements Runnable {
* @param requests * @param requests
* The profile requests. * The profile requests.
*/ */
private void fetchRequests(final Collection<ProfileRequest> requests) { private void fetchRequests(Collection<ProfileRequest> requests) {
Preconditions.checkNotNull(requests); Preconditions.checkNotNull(requests);
String[] playerNames = new String[requests.size()]; String[] playerNames = new String[requests.size()];
@ -126,7 +124,6 @@ class ProfileFetchThread implements Runnable {
for (ProfileRequest request : requests) { for (ProfileRequest request : requests) {
playerNames[i++] = request.getPlayerName(); playerNames[i++] = request.getPlayerName();
} }
NMS.findProfilesByNames(playerNames, new ProfileLookupCallback() { NMS.findProfilesByNames(playerNames, new ProfileLookupCallback() {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void onProfileLookupFailed(GameProfile profile, Exception e) { public void onProfileLookupFailed(GameProfile profile, Exception e) {
@ -139,7 +136,6 @@ class ProfileFetchThread implements Runnable {
Messaging.debug("Profile lookup for player '" + profileName + "' failed: " + getExceptionMsg(e)); Messaging.debug("Profile lookup for player '" + profileName + "' failed: " + getExceptionMsg(e));
Messaging.debug(Throwables.getStackTraceAsString(e)); Messaging.debug(Throwables.getStackTraceAsString(e));
} }
ProfileRequest request = findRequest(profileName, requests); ProfileRequest request = findRequest(profileName, requests);
if (request == null) if (request == null)
return; return;
@ -154,7 +150,7 @@ class ProfileFetchThread implements Runnable {
} }
@Override @Override
public void onProfileLookupSucceeded(final GameProfile profile) { public void onProfileLookupSucceeded(GameProfile profile) {
Messaging.idebug(() -> "Fetched profile " + profile.getId() + " for player " + profile.getName()); Messaging.idebug(() -> "Fetched profile " + profile.getId() + " for player " + profile.getName());
ProfileRequest request = findRequest(profile.getName(), requests); ProfileRequest request = findRequest(profile.getName(), requests);
@ -169,7 +165,6 @@ class ProfileFetchThread implements Runnable {
+ getExceptionMsg(e) + " " + isTooManyRequests(e)); + getExceptionMsg(e) + " " + isTooManyRequests(e));
Messaging.debug(Throwables.getStackTraceAsString(e)); Messaging.debug(Throwables.getStackTraceAsString(e));
} }
if (isTooManyRequests(e)) { if (isTooManyRequests(e)) {
request.setResult(null, ProfileFetchResult.TOO_MANY_REQUESTS); request.setResult(null, ProfileFetchResult.TOO_MANY_REQUESTS);
} else { } else {
@ -188,10 +183,9 @@ class ProfileFetchThread implements Runnable {
if (queue.isEmpty()) if (queue.isEmpty())
return; return;
requests = new ArrayList<ProfileRequest>(queue); requests = new ArrayList<>(queue);
queue.clear(); queue.clear();
} }
try { try {
fetchRequests(requests); fetchRequests(requests);
} catch (Exception ex) { } catch (Exception ex) {
@ -202,7 +196,7 @@ class ProfileFetchThread implements Runnable {
} }
} }
private static void addHandler(final ProfileRequest request, final ProfileFetchHandler handler) { private static void addHandler(ProfileRequest request, ProfileFetchHandler handler) {
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> request.addHandler(handler), 1); Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> request.addHandler(handler), 1);
} }
@ -211,9 +205,8 @@ class ProfileFetchThread implements Runnable {
name = name.toLowerCase(); name = name.toLowerCase();
for (ProfileRequest request : requests) { for (ProfileRequest request : requests) {
if (request.getPlayerName().equals(name)) { if (request.getPlayerName().equals(name))
return request; return request;
}
} }
return null; return null;
} }
@ -226,19 +219,18 @@ class ProfileFetchThread implements Runnable {
String message = e.getMessage(); String message = e.getMessage();
String cause = e.getCause() != null ? e.getCause().getMessage() : null; String cause = e.getCause() != null ? e.getCause().getMessage() : null;
return (message != null && message.contains("did not find")) return message != null && message.contains("did not find") || cause != null && cause.contains("did not find");
|| (cause != null && cause.contains("did not find"));
} }
private static boolean isTooManyRequests(Throwable e) { private static boolean isTooManyRequests(Throwable e) {
String message = e.getMessage(); String message = e.getMessage();
String cause = e.getCause() != null ? e.getCause().getMessage() : null; String cause = e.getCause() != null ? e.getCause().getMessage() : null;
return (message != null && message.contains("too many requests")) return message != null && message.contains("too many requests")
|| (cause != null && cause.contains("too many requests")); || cause != null && cause.contains("too many requests");
} }
private static void sendResult(final ProfileFetchHandler handler, final ProfileRequest request) { private static void sendResult(ProfileFetchHandler handler, ProfileRequest request) {
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> handler.onResult(request), 1); Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> handler.onResult(request), 1);
} }
} }

View File

@ -48,7 +48,6 @@ public class ProfileFetcher {
if (THREAD_TASK != null) { if (THREAD_TASK != null) {
THREAD_TASK.cancel(); THREAD_TASK.cancel();
} }
PROFILE_THREAD = new ProfileFetchThread(); PROFILE_THREAD = new ProfileFetchThread();
THREAD_TASK = Bukkit.getScheduler().runTaskTimerAsynchronously(CitizensAPI.getPlugin(), PROFILE_THREAD, 21, 20); THREAD_TASK = Bukkit.getScheduler().runTaskTimerAsynchronously(CitizensAPI.getPlugin(), PROFILE_THREAD, 21, 20);
} }

View File

@ -61,10 +61,9 @@ public class ProfileRequest {
handler.onResult(this); handler.onResult(this);
return; return;
} }
if (handlers == null) {
if (handlers == null) handlers = new ArrayDeque<>();
handlers = new ArrayDeque<ProfileFetchHandler>(); }
handlers.addLast(handler); handlers.addLast(handler);
} }
@ -105,24 +104,20 @@ public class ProfileRequest {
* @param result * @param result
* The result of the request. * The result of the request.
*/ */
void setResult(final @Nullable GameProfile profile, final ProfileFetchResult result) { void setResult(@Nullable GameProfile profile, ProfileFetchResult result) {
if (!CitizensAPI.hasImplementation()) if (!CitizensAPI.hasImplementation())
return; return;
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() { Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> {
@Override ProfileRequest.this.profile = profile;
public void run() { ProfileRequest.this.result = result;
ProfileRequest.this.profile = profile;
ProfileRequest.this.result = result;
if (handlers == null) if (handlers == null)
return; return;
while (!handlers.isEmpty()) { while (!handlers.isEmpty()) {
handlers.removeFirst().onResult(ProfileRequest.this); handlers.removeFirst().onResult(ProfileRequest.this);
}
handlers = null;
} }
handlers = null;
}); });
} }
} }

View File

@ -22,9 +22,7 @@ import net.citizensnpcs.api.event.DespawnReason;
import net.citizensnpcs.api.event.SpawnReason; import net.citizensnpcs.api.event.SpawnReason;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.npc.profile.ProfileFetchHandler;
import net.citizensnpcs.npc.profile.ProfileFetcher; import net.citizensnpcs.npc.profile.ProfileFetcher;
import net.citizensnpcs.npc.profile.ProfileRequest;
import net.citizensnpcs.trait.SkinTrait; import net.citizensnpcs.trait.SkinTrait;
import net.citizensnpcs.util.SkinProperty; import net.citizensnpcs.util.SkinProperty;
@ -36,7 +34,7 @@ public class Skin {
private int fetchRetries = -1; private int fetchRetries = -1;
private boolean hasFetched; private boolean hasFetched;
private volatile boolean isValid = true; private volatile boolean isValid = true;
private final Map<SkinnableEntity, Void> pending = new WeakHashMap<SkinnableEntity, Void>(15); private final Map<SkinnableEntity, Void> pending = new WeakHashMap<>(15);
private BukkitTask retryTask; private BukkitTask retryTask;
private volatile SkinProperty skinData; private volatile SkinProperty skinData;
private volatile UUID skinId; private volatile UUID skinId;
@ -58,7 +56,6 @@ public class Skin {
CACHE.put(this.skinName, this); CACHE.put(this.skinName, this);
} }
// fetch(); // fetch();
} }
@ -84,29 +81,27 @@ public class Skin {
// If npc requires latest skin, cache is used for faster availability until the latest skin can be loaded. // If npc requires latest skin, cache is used for faster availability until the latest skin can be loaded.
String cachedName = npc.data().get(CACHED_SKIN_UUID_NAME_METADATA); String cachedName = npc.data().get(CACHED_SKIN_UUID_NAME_METADATA);
String texture = skinTrait.getTexture(); String texture = skinTrait.getTexture();
if (this.skinName.equals(cachedName) && texture != null && !texture.equals("cache")) { if (skinName.equals(cachedName) && texture != null && !texture.equals("cache")) {
setNPCTexture(entity, new SkinProperty("textures", texture, skinTrait.getSignature())); setNPCTexture(entity, new SkinProperty("textures", texture, skinTrait.getSignature()));
// check if NPC prefers to use cached skin over the latest skin. // check if NPC prefers to use cached skin over the latest skin.
if (entity.getNPC().data().has("player-skin-use-latest")) { if (entity.getNPC().data().has("player-skin-use-latest")) {
entity.getNPC().data().remove("player-skin-use-latest"); entity.getNPC().data().remove("player-skin-use-latest");
} }
if (!skinTrait.shouldUpdateSkins()) { if (!skinTrait.shouldUpdateSkins())
// cache preferred // cache preferred
return true; return true;
}
} }
if (!hasSkinData()) { if (!hasSkinData()) {
String defaultSkinName = ChatColor.stripColor(npc.getName()).toLowerCase(); String defaultSkinName = ChatColor.stripColor(npc.getName()).toLowerCase();
if (npc.hasTrait(SkinTrait.class) && this.skinName.equals(defaultSkinName) if (npc.hasTrait(SkinTrait.class) && skinName.equals(defaultSkinName)
&& !npc.getOrAddTrait(SkinTrait.class).fetchDefaultSkin()) && !npc.getOrAddTrait(SkinTrait.class).fetchDefaultSkin())
return false; return false;
if (hasFetched) { if (hasFetched)
return true; return true;
} else { else {
if (!fetching) { if (!fetching) {
fetch(); fetch();
} }
@ -114,7 +109,6 @@ public class Skin {
return false; return false;
} }
} }
setNPCSkinData(entity, skinName, skinId, skinData); setNPCSkinData(entity, skinName, skinId, skinData);
return true; return true;
@ -144,64 +138,54 @@ public class Skin {
} }
private void fetch() { private void fetch() {
final int maxRetries = Setting.MAX_NPC_SKIN_RETRIES.asInt(); int maxRetries = Setting.MAX_NPC_SKIN_RETRIES.asInt();
if (maxRetries > -1 && fetchRetries >= maxRetries) { if (maxRetries > -1 && fetchRetries >= maxRetries) {
if (Messaging.isDebugging()) { if (Messaging.isDebugging()) {
Messaging.debug("Reached max skin fetch retries for '" + skinName + "'"); Messaging.debug("Reached max skin fetch retries for '" + skinName + "'");
} }
return; return;
} }
if (skinName.length() < 3 || skinName.length() > 16) { if (skinName.length() < 3 || skinName.length() > 16) {
if (Messaging.isDebugging()) { if (Messaging.isDebugging()) {
Messaging.debug("Skin name invalid length '" + skinName + "'"); Messaging.debug("Skin name invalid length '" + skinName + "'");
} }
return; return;
} }
if (skinName.toLowerCase().startsWith("cit-"))
if (skinName.toLowerCase().startsWith("cit-")) {
return; return;
}
fetching = true; fetching = true;
ProfileFetcher.fetch(this.skinName, new ProfileFetchHandler() { ProfileFetcher.fetch(skinName, request -> {
@Override hasFetched = true;
public void onResult(ProfileRequest request) {
hasFetched = true;
switch (request.getResult()) { switch (request.getResult()) {
case NOT_FOUND: case NOT_FOUND:
isValid = false; isValid = false;
break;
case TOO_MANY_REQUESTS:
if (maxRetries == 0) {
break; break;
case TOO_MANY_REQUESTS: }
if (maxRetries == 0) { fetchRetries++;
break; long delay = Setting.NPC_SKIN_RETRY_DELAY.asTicks();
} retryTask = Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), (Runnable) this::fetch,
fetchRetries++; delay);
long delay = Setting.NPC_SKIN_RETRY_DELAY.asTicks();
retryTask = Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), new Runnable() {
@Override
public void run() {
fetch();
}
}, delay);
Messaging.idebug(() -> "Retrying skin fetch for '" + skinName + "' in " + delay + " ticks."); Messaging.idebug(() -> "Retrying skin fetch for '" + skinName + "' in " + delay + " ticks.");
break; break;
case SUCCESS: case SUCCESS:
GameProfile profile = request.getProfile(); GameProfile profile = request.getProfile();
setData(profile); setData(profile);
break; break;
default: default:
break; break;
}
} }
}); });
} }
private void fetchForced() { private void fetchForced() {
final int maxRetries = Setting.MAX_NPC_SKIN_RETRIES.asInt(); int maxRetries = Setting.MAX_NPC_SKIN_RETRIES.asInt();
if (maxRetries > -1 && fetchRetries >= maxRetries) { if (maxRetries > -1 && fetchRetries >= maxRetries) {
Messaging.idebug(() -> "Reached max skin fetch retries for '" + skinName + "'"); Messaging.idebug(() -> "Reached max skin fetch retries for '" + skinName + "'");
return; return;
@ -210,44 +194,35 @@ public class Skin {
Messaging.idebug(() -> "Skin name invalid length '" + skinName + "'"); Messaging.idebug(() -> "Skin name invalid length '" + skinName + "'");
return; return;
} }
if (skinName.toLowerCase().startsWith("cit-"))
if (skinName.toLowerCase().startsWith("cit-")) {
return; return;
}
fetching = true; fetching = true;
ProfileFetcher.fetchForced(this.skinName, new ProfileFetchHandler() { ProfileFetcher.fetchForced(skinName, request -> {
@Override hasFetched = true;
public void onResult(ProfileRequest request) {
hasFetched = true;
switch (request.getResult()) { switch (request.getResult()) {
case NOT_FOUND: case NOT_FOUND:
isValid = false; isValid = false;
break;
case TOO_MANY_REQUESTS:
if (maxRetries == 0) {
break; break;
case TOO_MANY_REQUESTS: }
if (maxRetries == 0) { fetchRetries++;
break; int delay = Setting.NPC_SKIN_RETRY_DELAY.asTicks();
} retryTask = Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(),
fetchRetries++; (Runnable) this::fetchForced, delay);
int delay = Setting.NPC_SKIN_RETRY_DELAY.asTicks();
retryTask = Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), new Runnable() {
@Override
public void run() {
fetchForced();
}
}, delay);
Messaging.idebug(() -> "Retrying skin fetch for '" + skinName + "' in " + delay + " ticks."); Messaging.idebug(() -> "Retrying skin fetch for '" + skinName + "' in " + delay + " ticks.");
break; break;
case SUCCESS: case SUCCESS:
GameProfile profile = request.getProfile(); GameProfile profile = request.getProfile();
setData(profile); setData(profile);
break; break;
default: default:
break; break;
}
} }
}); });
} }
@ -288,16 +263,14 @@ public class Skin {
isValid = false; isValid = false;
return; return;
} }
if (!profile.getName().toLowerCase().equals(skinName)) { if (!profile.getName().toLowerCase().equals(skinName)) {
Messaging.debug("GameProfile name (" + profile.getName() + ") and " + "skin name (" + skinName Messaging.debug("GameProfile name (" + profile.getName() + ") and " + "skin name (" + skinName
+ ") do not match. Has the user renamed recently?"); + ") do not match. Has the user renamed recently?");
} }
skinId = profile.getId(); skinId = profile.getId();
skinData = SkinProperty.fromMojangProfile(profile); skinData = SkinProperty.fromMojangProfile(profile);
List<SkinnableEntity> entities = new ArrayList<SkinnableEntity>(pending.keySet()); List<SkinnableEntity> entities = new ArrayList<>(pending.keySet());
for (SkinnableEntity entity : entities) { for (SkinnableEntity entity : entities) {
applyAndRespawn(entity); applyAndRespawn(entity);
} }
@ -371,13 +344,11 @@ public class Skin {
synchronized (CACHE) { synchronized (CACHE) {
skin = CACHE.get(skinName); skin = CACHE.get(skinName);
} }
if (skin == null) { if (skin == null) {
skin = new Skin(skinName); skin = new Skin(skinName);
} else if (forceUpdate) { } else if (forceUpdate) {
skin.fetchForced(); skin.fetchForced();
} }
return skin; return skin;
} }
@ -405,14 +376,13 @@ public class Skin {
// packet errors that disconnect the client. // packet errors that disconnect the client.
SkinProperty current = SkinProperty.fromMojangProfile(profile); SkinProperty current = SkinProperty.fromMojangProfile(profile);
if (current != null && current.value.equals(skinProperty.value) && current.signature != null if (current != null && current.value.equals(skinProperty.value) && current.signature != null
&& current.signature.equals(skinProperty.signature)) { && current.signature.equals(skinProperty.signature))
return; return;
}
skinProperty.apply(profile); skinProperty.apply(profile);
} }
private static final Map<String, Skin> CACHE = new HashMap<String, Skin>(20); private static Map<String, Skin> CACHE = new HashMap<>(20);
public static final String CACHED_SKIN_UUID_METADATA = "cached-skin-uuid"; public static String CACHED_SKIN_UUID_METADATA = "cached-skin-uuid";
public static final String CACHED_SKIN_UUID_NAME_METADATA = "cached-skin-uuid-name"; public static String CACHED_SKIN_UUID_NAME_METADATA = "cached-skin-uuid-name";
} }

View File

@ -28,7 +28,7 @@ import net.citizensnpcs.util.NMS;
*/ */
public class SkinPacketTracker { public class SkinPacketTracker {
private final SkinnableEntity entity; private final SkinnableEntity entity;
private final Map<UUID, PlayerEntry> inProgress = new HashMap<UUID, PlayerEntry>( private final Map<UUID, PlayerEntry> inProgress = new HashMap<>(
Math.max(128, Math.min(1024, Bukkit.getMaxPlayers() / 2))); Math.max(128, Math.min(1024, Bukkit.getMaxPlayers() / 2)));
private boolean isRemoved; private boolean isRemoved;
private Skin skin; private Skin skin;
@ -43,7 +43,7 @@ public class SkinPacketTracker {
Preconditions.checkNotNull(entity); Preconditions.checkNotNull(entity);
this.entity = entity; this.entity = entity;
this.skin = Skin.get(entity); skin = Skin.get(entity);
if (LISTENER == null) { if (LISTENER == null) {
LISTENER = new PlayerListener(); LISTENER = new PlayerListener();
@ -76,10 +76,7 @@ public class SkinPacketTracker {
*/ */
void notifyRemovePacketSent(UUID playerId) { void notifyRemovePacketSent(UUID playerId) {
PlayerEntry entry = inProgress.get(playerId); PlayerEntry entry = inProgress.get(playerId);
if (entry == null) if (entry == null || entry.removeCount == 0)
return;
if (entry.removeCount == 0)
return; return;
entry.removeCount -= 1; entry.removeCount -= 1;
@ -94,7 +91,7 @@ public class SkinPacketTracker {
* Notify that the NPC skin has been changed. * Notify that the NPC skin has been changed.
*/ */
public void notifySkinChange(boolean forceUpdate) { public void notifySkinChange(boolean forceUpdate) {
this.skin = Skin.get(entity, forceUpdate); skin = Skin.get(entity, forceUpdate);
skin.applyAndRespawn(entity); skin.applyAndRespawn(entity);
} }
@ -111,9 +108,9 @@ public class SkinPacketTracker {
Collection<? extends Player> players = Bukkit.getOnlinePlayers(); Collection<? extends Player> players = Bukkit.getOnlinePlayers();
for (Player player : players) { for (Player player : players) {
if (player.hasMetadata("NPC")) if (player.hasMetadata("NPC")) {
continue; continue;
}
// send packet now and later to ensure removal from player list // send packet now and later to ensure removal from player list
NMS.sendTabListRemove(player, entity.getBukkitEntity()); NMS.sendTabListRemove(player, entity.getBukkitEntity());
TAB_LIST_REMOVER.sendPacket(player, entity); TAB_LIST_REMOVER.sendPacket(player, entity);
@ -137,7 +134,7 @@ public class SkinPacketTracker {
}.runTaskLater(CitizensAPI.getPlugin(), 15); }.runTaskLater(CitizensAPI.getPlugin(), 15);
} }
private void scheduleRemovePacket(final PlayerEntry entry) { private void scheduleRemovePacket(PlayerEntry entry) {
if (isRemoved || !CitizensAPI.hasImplementation() || !CitizensAPI.getPlugin().isEnabled() if (isRemoved || !CitizensAPI.hasImplementation() || !CitizensAPI.getPlugin().isEnabled()
|| !shouldRemoveFromTabList()) || !shouldRemoveFromTabList())
return; return;
@ -177,7 +174,7 @@ public class SkinPacketTracker {
* @param player * @param player
* The player. * The player.
*/ */
public void updateViewer(final Player player) { public void updateViewer(Player player) {
Preconditions.checkNotNull(player); Preconditions.checkNotNull(player);
if (isRemoved || player.hasMetadata("NPC")) if (isRemoved || player.hasMetadata("NPC"))
@ -189,7 +186,6 @@ public class SkinPacketTracker {
} else { } else {
entry = new PlayerEntry(player); entry = new PlayerEntry(player);
} }
TAB_LIST_REMOVER.cancelPackets(player, entity); TAB_LIST_REMOVER.cancelPackets(player, entity);
inProgress.put(player.getUniqueId(), entry); inProgress.put(player.getUniqueId(), entry);
@ -199,7 +195,7 @@ public class SkinPacketTracker {
} }
} }
private class PlayerEntry { private static class PlayerEntry {
Player player; Player player;
int removeCount; int removeCount;
BukkitTask removeTask; BukkitTask removeTask;
@ -228,6 +224,6 @@ public class SkinPacketTracker {
} }
private static PlayerListener LISTENER; private static PlayerListener LISTENER;
private static final int PACKET_DELAY_REMOVE = 2; private static int PACKET_DELAY_REMOVE = 2;
private static final TabListRemover TAB_LIST_REMOVER = new TabListRemover(); private static TabListRemover TAB_LIST_REMOVER = new TabListRemover();
} }

View File

@ -37,8 +37,8 @@ import net.citizensnpcs.util.Util;
* @see net.citizensnpcs.EventListen * @see net.citizensnpcs.EventListen
*/ */
public class SkinUpdateTracker { public class SkinUpdateTracker {
private final Map<SkinnableEntity, Void> navigating = new WeakHashMap<SkinnableEntity, Void>(25); private final Map<SkinnableEntity, Void> navigating = new WeakHashMap<>(25);
private final Map<UUID, PlayerTracker> playerTrackers = new HashMap<UUID, PlayerTracker>( private final Map<UUID, PlayerTracker> playerTrackers = new HashMap<>(
Math.max(128, Math.min(1024, Bukkit.getMaxPlayers() / 2))); Math.max(128, Math.min(1024, Bukkit.getMaxPlayers() / 2)));
private final NPCNavigationUpdater updater = new NPCNavigationUpdater(); private final NPCNavigationUpdater updater = new NPCNavigationUpdater();
@ -57,13 +57,7 @@ public class SkinUpdateTracker {
// skinnable entity is within the player's field of view. // skinnable entity is within the player's field of view.
private boolean canSee(Player player, SkinnableEntity skinnable, boolean checkFov) { private boolean canSee(Player player, SkinnableEntity skinnable, boolean checkFov) {
Player entity = skinnable.getBukkitEntity(); Player entity = skinnable.getBukkitEntity();
if (entity == null) if (entity == null || !player.canSee(entity) || !player.getWorld().equals(entity.getWorld()))
return false;
if (!player.canSee(entity))
return false;
if (!player.getWorld().equals(entity.getWorld()))
return false; return false;
Location playerLoc = player.getLocation(); Location playerLoc = player.getLocation();
@ -92,7 +86,6 @@ public class SkinUpdateTracker {
} }
return hasMoved; return hasMoved;
} }
return true; return true;
} }
@ -101,17 +94,14 @@ public class SkinUpdateTracker {
} }
private List<SkinnableEntity> getNearbyNPCs(Player player, boolean reset, boolean checkFov) { private List<SkinnableEntity> getNearbyNPCs(Player player, boolean reset, boolean checkFov) {
List<SkinnableEntity> results = new ArrayList<SkinnableEntity>(); List<SkinnableEntity> results = new ArrayList<>();
PlayerTracker tracker = getTracker(player, reset); PlayerTracker tracker = getTracker(player, reset);
for (NPC npc : getAllNPCs()) { for (NPC npc : getAllNPCs()) {
SkinnableEntity skinnable = getSkinnable(npc); 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 checking field of view, don't add skins that have already been updated for FOV
if (checkFov && tracker.fovVisibleSkins.contains(skinnable)) if (skinnable == null || checkFov && tracker.fovVisibleSkins.contains(skinnable)) {
continue; continue;
}
if (canSee(player, skinnable, checkFov)) { if (canSee(player, skinnable, checkFov)) {
results.add(skinnable); results.add(skinnable);
} }
@ -126,9 +116,9 @@ public class SkinUpdateTracker {
for (SkinnableEntity skinnable : navigating.keySet()) { for (SkinnableEntity skinnable : navigating.keySet()) {
// make sure player hasn't already been updated to prevent excessive tab list flashing // 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. // while NPC's are navigating and to reduce the number of times #canSee is invoked.
if (tracker.fovVisibleSkins.contains(skinnable)) if (tracker.fovVisibleSkins.contains(skinnable)) {
continue; continue;
}
if (canSee(player, skinnable, true)) { if (canSee(player, skinnable, true)) {
output.add(skinnable); output.add(skinnable);
} }
@ -231,10 +221,7 @@ public class SkinUpdateTracker {
public void onPlayerMove(Player player) { public void onPlayerMove(Player player) {
Preconditions.checkNotNull(player); Preconditions.checkNotNull(player);
PlayerTracker updateTracker = playerTrackers.get(player.getUniqueId()); PlayerTracker updateTracker = playerTrackers.get(player.getUniqueId());
if (updateTracker == null) if (updateTracker == null || !updateTracker.shouldUpdate(player))
return;
if (!updateTracker.shouldUpdate(player))
return; return;
updatePlayer(player, 10, false); updatePlayer(player, 10, false);
@ -277,14 +264,13 @@ public class SkinUpdateTracker {
Location location = entity.getLocation(); Location location = entity.getLocation();
List<Player> players = entity.getWorld().getPlayers(); List<Player> players = entity.getWorld().getPlayers();
for (Player player : players) { for (Player player : players) {
if (player.hasMetadata("NPC")) if (player.hasMetadata("NPC")) {
continue; continue;
}
Location ploc = player.getLocation(); Location ploc = player.getLocation();
if (ploc.getWorld() != location.getWorld()) if (ploc.getWorld() != location.getWorld() || ploc.distance(location) > viewDistance) {
continue; continue;
if (ploc.distance(location) > viewDistance) }
continue;
PlayerTracker tracker = playerTrackers.get(player.getUniqueId()); PlayerTracker tracker = playerTrackers.get(player.getUniqueId());
if (tracker != null) { if (tracker != null) {
tracker.hardReset(player); tracker.hardReset(player);
@ -302,7 +288,7 @@ public class SkinUpdateTracker {
* @param reset * @param reset
* True to hard reset the players tracking info, otherwise false. * True to hard reset the players tracking info, otherwise false.
*/ */
public void updatePlayer(final Player player, long delay, final boolean reset) { public void updatePlayer(Player player, long delay, boolean reset) {
if (player.hasMetadata("NPC")) if (player.hasMetadata("NPC"))
return; return;
@ -328,13 +314,13 @@ public class SkinUpdateTracker {
if (navigating.isEmpty() || playerTrackers.isEmpty()) if (navigating.isEmpty() || playerTrackers.isEmpty())
return; return;
List<SkinnableEntity> nearby = new ArrayList<SkinnableEntity>(10); List<SkinnableEntity> nearby = new ArrayList<>(10);
Set<UUID> seen = Sets.newHashSet(); Set<UUID> seen = Sets.newHashSet();
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
seen.add(player.getUniqueId()); seen.add(player.getUniqueId());
if (player.hasMetadata("NPC")) if (player.hasMetadata("NPC")) {
continue; continue;
}
getNewVisibleNavigating(player, nearby); getNewVisibleNavigating(player, nearby);
for (SkinnableEntity skinnable : nearby) { for (SkinnableEntity skinnable : nearby) {
@ -342,7 +328,6 @@ public class SkinUpdateTracker {
tracker.fovVisibleSkins.add(skinnable); tracker.fovVisibleSkins.add(skinnable);
updater.queue.offer(new UpdateInfo(player, skinnable)); updater.queue.offer(new UpdateInfo(player, skinnable));
} }
nearby.clear(); nearby.clear();
} }
playerTrackers.keySet().removeIf(uuid -> !seen.contains(uuid)); playerTrackers.keySet().removeIf(uuid -> !seen.contains(uuid));
@ -351,8 +336,8 @@ public class SkinUpdateTracker {
// Updates players. Repeating task used to schedule updates without // Updates players. Repeating task used to schedule updates without
// causing excessive scheduling. // causing excessive scheduling.
private class NPCNavigationUpdater extends BukkitRunnable { private static class NPCNavigationUpdater extends BukkitRunnable {
Queue<UpdateInfo> queue = new ArrayDeque<UpdateInfo>(20); Queue<UpdateInfo> queue = new ArrayDeque<>(20);
@Override @Override
public void run() { public void run() {
@ -365,10 +350,10 @@ public class SkinUpdateTracker {
// Tracks player location and yaw to determine when the player should be updated // Tracks player location and yaw to determine when the player should be updated
// with nearby skins. // with nearby skins.
private class PlayerTracker { private static class PlayerTracker {
final Set<SkinnableEntity> fovVisibleSkins = new HashSet<SkinnableEntity>(10); Set<SkinnableEntity> fovVisibleSkins = new HashSet<>(10);
boolean hasMoved; boolean hasMoved;
final Location location = new Location(null, 0, 0, 0); Location location = new Location(null, 0, 0, 0);
float lowerBound; float lowerBound;
int rotationCount; int rotationCount;
float startYaw; float startYaw;
@ -380,22 +365,22 @@ public class SkinUpdateTracker {
// reset all // reset all
void hardReset(Player player) { void hardReset(Player player) {
this.hasMoved = false; hasMoved = false;
this.rotationCount = 0; rotationCount = 0;
this.lowerBound = this.upperBound = this.startYaw = 0; lowerBound = upperBound = startYaw = 0;
this.fovVisibleSkins.clear(); fovVisibleSkins.clear();
reset(player); reset(player);
} }
// resets initial yaw and location to the players current location and yaw. // resets initial yaw and location to the players current location and yaw.
void reset(Player player) { void reset(Player player) {
player.getLocation(this.location); player.getLocation(location);
if (rotationCount < 3) { if (rotationCount < 3) {
float rotationDegrees = Setting.NPC_SKIN_ROTATION_UPDATE_DEGREES.asFloat(); float rotationDegrees = Setting.NPC_SKIN_ROTATION_UPDATE_DEGREES.asFloat();
float yaw = Util.clamp(this.location.getYaw()); float yaw = Util.clamp(location.getYaw());
this.startYaw = yaw; startYaw = yaw;
this.upperBound = Util.clamp(yaw + rotationDegrees); upperBound = Util.clamp(yaw + rotationDegrees);
this.lowerBound = Util.clamp(yaw - rotationDegrees); lowerBound = Util.clamp(yaw - rotationDegrees);
if (upperBound == -180.0 && startYaw > 0) { if (upperBound == -180.0 && startYaw > 0) {
upperBound = 0; upperBound = 0;
} }
@ -406,16 +391,14 @@ public class SkinUpdateTracker {
Location currentLoc = player.getLocation(); Location currentLoc = player.getLocation();
// make sure player is in same world // make sure player is in same world
if (!currentLoc.getWorld().equals(this.location.getWorld())) { if (!currentLoc.getWorld().equals(location.getWorld())) {
hardReset(player); hardReset(player);
return true; return true;
} }
if (!hasMoved) { if (!hasMoved) {
hasMoved = true; hasMoved = true;
return true; return true;
} }
if (rotationCount < 3) { if (rotationCount < 3) {
float yaw = Util.clamp(currentLoc.getYaw()); float yaw = Util.clamp(currentLoc.getYaw());
boolean hasRotated; boolean hasRotated;
@ -424,7 +407,6 @@ public class SkinUpdateTracker {
} else { } else {
hasRotated = yaw < lowerBound || yaw > upperBound; hasRotated = yaw < lowerBound || yaw > upperBound;
} }
// update the first 3 times the player rotates. helps load skins around player // update the first 3 times the player rotates. helps load skins around player
// after the player logs/teleports. // after the player logs/teleports.
if (hasRotated) { if (hasRotated) {
@ -433,14 +415,12 @@ public class SkinUpdateTracker {
return true; return true;
} }
} }
// update every time a player moves a certain distance // update every time a player moves a certain distance
if (currentLoc.distance(this.location) > MOVEMENT_SKIN_UPDATE_DISTANCE) { if (currentLoc.distance(location) > MOVEMENT_SKIN_UPDATE_DISTANCE) {
reset(player); reset(player);
return true; return true;
} else { } else
return false; return false;
}
} }
} }
@ -454,6 +434,6 @@ public class SkinUpdateTracker {
} }
} }
private static final float FIELD_OF_VIEW = 70F; private static float FIELD_OF_VIEW = 70F;
private static final int MOVEMENT_SKIN_UPDATE_DISTANCE = 25; private static int MOVEMENT_SKIN_UPDATE_DISTANCE = 25;
} }

View File

@ -26,7 +26,7 @@ import net.citizensnpcs.util.NMS;
* </p> * </p>
*/ */
public class TabListRemover { public class TabListRemover {
private final Map<UUID, PlayerEntry> pending = new HashMap<UUID, PlayerEntry>( private final Map<UUID, PlayerEntry> pending = new HashMap<>(
Math.max(128, Math.min(1024, Bukkit.getMaxPlayers() / 2))); Math.max(128, Math.min(1024, Bukkit.getMaxPlayers() / 2)));
TabListRemover() { TabListRemover() {
@ -70,7 +70,6 @@ public class TabListRemover {
if (entry.toRemove.remove(skinnable)) { if (entry.toRemove.remove(skinnable)) {
skinnable.getSkinTracker().notifyRemovePacketCancelled(player.getUniqueId()); skinnable.getSkinTracker().notifyRemovePacketCancelled(player.getUniqueId());
} }
if (entry.toRemove.isEmpty()) { if (entry.toRemove.isEmpty()) {
pending.remove(player.getUniqueId()); pending.remove(player.getUniqueId());
} }
@ -82,7 +81,6 @@ public class TabListRemover {
entry = new PlayerEntry(player); entry = new PlayerEntry(player);
pending.put(player.getUniqueId(), entry); pending.put(player.getUniqueId(), entry);
} }
return entry; return entry;
} }
@ -103,9 +101,9 @@ public class TabListRemover {
entry.toRemove.add(entity); entry.toRemove.add(entity);
} }
private class PlayerEntry { private static class PlayerEntry {
Player player; Player player;
Set<SkinnableEntity> toRemove = new HashSet<SkinnableEntity>(20); Set<SkinnableEntity> toRemove = new HashSet<>(20);
PlayerEntry(Player player) { PlayerEntry(Player player) {
this.player = player; this.player = player;
@ -125,30 +123,27 @@ public class TabListRemover {
int listSize = Math.min(maxPacketEntries, entry.toRemove.size()); int listSize = Math.min(maxPacketEntries, entry.toRemove.size());
boolean sendAll = listSize == entry.toRemove.size(); boolean sendAll = listSize == entry.toRemove.size();
List<SkinnableEntity> skinnableList = new ArrayList<SkinnableEntity>(listSize); List<SkinnableEntity> skinnableList = new ArrayList<>(listSize);
int i = 0; int i = 0;
Iterator<SkinnableEntity> skinIterator = entry.toRemove.iterator(); Iterator<SkinnableEntity> skinIterator = entry.toRemove.iterator();
while (skinIterator.hasNext()) { while (skinIterator.hasNext()) {
if (i >= maxPacketEntries) if (i >= maxPacketEntries) {
break; break;
}
SkinnableEntity skinnable = skinIterator.next(); SkinnableEntity skinnable = skinIterator.next();
skinnableList.add(skinnable); skinnableList.add(skinnable);
skinIterator.remove(); skinIterator.remove();
i++; i++;
} }
if (entry.player.isOnline()) { if (entry.player.isOnline()) {
NMS.sendTabListRemove(entry.player, skinnableList); NMS.sendTabListRemove(entry.player, skinnableList);
} }
// notify skin trackers that a remove packet has been sent to a player // notify skin trackers that a remove packet has been sent to a player
for (SkinnableEntity entity : skinnableList) { for (SkinnableEntity entity : skinnableList) {
entity.getSkinTracker().notifyRemovePacketSent(entry.player.getUniqueId()); entity.getSkinTracker().notifyRemovePacketSent(entry.player.getUniqueId());
} }
if (sendAll) { if (sendAll) {
entryIterator.remove(); entryIterator.remove();
} }

View File

@ -50,13 +50,12 @@ public class Age extends Trait implements Toggleable {
entity.setAge(age); entity.setAge(age);
entity.setAgeLock(locked); entity.setAgeLock(locked);
ageable = entity; ageable = entity;
} else if (npc.getEntity() instanceof Zombie) {
((Zombie) npc.getEntity()).setBaby(age < 0);
ageable = null;
} else if (npc.isSpawned() && npc.getEntity().getType().name().equals("TADPOLE")) {
((Tadpole) npc.getEntity()).setAge(age);
ageable = null;
} else { } else {
if (npc.getEntity() instanceof Zombie) {
((Zombie) npc.getEntity()).setBaby(age < 0);
} else if (npc.isSpawned() && npc.getEntity().getType().name().equals("TADPOLE")) {
((Tadpole) npc.getEntity()).setAge(age);
}
ageable = null; ageable = null;
} }
} }
@ -78,7 +77,6 @@ public class Age extends Trait implements Toggleable {
if (isAgeable()) { if (isAgeable()) {
ageable.setAgeLock(locked); ageable.setAgeLock(locked);
} }
} }
/** /**

View File

@ -21,7 +21,7 @@ import net.citizensnpcs.util.Messages;
*/ */
@TraitName("anchors") @TraitName("anchors")
public class Anchors extends Trait { public class Anchors extends Trait {
private final List<Anchor> anchors = new ArrayList<Anchor>(); private final List<Anchor> anchors = new ArrayList<>();
public Anchors() { public Anchors() {
super("anchors"); super("anchors");
@ -61,8 +61,8 @@ public class Anchors extends Trait {
String[] parts = sub.getString("").split(";"); String[] parts = sub.getString("").split(";");
Location location; Location location;
try { try {
location = new Location(Bukkit.getServer().getWorld(parts[1]), Double.valueOf(parts[2]), location = new Location(Bukkit.getServer().getWorld(parts[1]), Double.parseDouble(parts[2]),
Double.valueOf(parts[3]), Double.valueOf(parts[4])); Double.parseDouble(parts[3]), Double.parseDouble(parts[4]));
anchors.add(new Anchor(parts[0], location)); anchors.add(new Anchor(parts[0], location));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Messaging.logTr(Messages.SKIPPING_INVALID_ANCHOR, sub.name(), e.getMessage()); Messaging.logTr(Messages.SKIPPING_INVALID_ANCHOR, sub.name(), e.getMessage());
@ -85,8 +85,9 @@ public class Anchors extends Trait {
@Override @Override
public void save(DataKey key) { public void save(DataKey key) {
key.removeKey("list"); key.removeKey("list");
for (int i = 0; i < anchors.size(); i++) for (int i = 0; i < anchors.size(); i++) {
key.setString("list." + String.valueOf(i), anchors.get(i).stringValue()); key.setString("list." + String.valueOf(i), anchors.get(i).stringValue());
}
} }
} }

View File

@ -158,14 +158,14 @@ public class ArmorStandTrait extends Trait {
* @see ArmorStand#setArms(boolean) * @see ArmorStand#setArms(boolean)
*/ */
public void setHasArms(boolean arms) { public void setHasArms(boolean arms) {
this.hasarms = arms; hasarms = arms;
} }
/** /**
* @see ArmorStand#setBasePlate(boolean) * @see ArmorStand#setBasePlate(boolean)
*/ */
public void setHasBaseplate(boolean baseplate) { public void setHasBaseplate(boolean baseplate) {
this.hasbaseplate = baseplate; hasbaseplate = baseplate;
} }
/** /**

View File

@ -67,7 +67,7 @@ public class BoundingBoxTrait extends Trait implements Supplier<BoundingBox> {
} }
public void setBoundingBoxFunction(Function<EntityDim, BoundingBox> func) { public void setBoundingBoxFunction(Function<EntityDim, BoundingBox> func) {
this.function = func; function = func;
} }
public void setHeight(float height) { public void setHeight(float height) {

View File

@ -25,7 +25,7 @@ public class ClickRedirectTrait extends Trait {
public ClickRedirectTrait(NPC npc) { public ClickRedirectTrait(NPC npc) {
this(); this();
this.redirectNPC = npc; redirectNPC = npc;
if (redirectNPC != null && redirectNPC.hasTrait(PlayerFilter.class)) { if (redirectNPC != null && redirectNPC.hasTrait(PlayerFilter.class)) {
redirectNPC.getOrAddTrait(PlayerFilter.class).addChildNPC(npc); redirectNPC.getOrAddTrait(PlayerFilter.class).addChildNPC(npc);
} }

View File

@ -3,6 +3,7 @@ package net.citizensnpcs.trait;
import java.time.Duration; import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -218,13 +219,11 @@ public class CommandTrait extends Trait {
} else { } else {
outputList.add(0, executionMode.toString()); outputList.add(0, executionMode.toString());
} }
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
for (String item : outputList) { for (String item : outputList) {
output.append(item); output.append(item);
output.append(" "); output.append(" ");
} }
Messaging.send(sender, output.toString().trim()); Messaging.send(sender, output.toString().trim());
} }
@ -252,9 +251,9 @@ public class CommandTrait extends Trait {
return output; return output;
} }
public void dispatch(final Player player, Hand handIn) { public void dispatch(Player player, Hand handIn) {
final Hand hand = player.isSneaking() Hand hand = player.isSneaking()
? (handIn == CommandTrait.Hand.LEFT ? CommandTrait.Hand.SHIFT_LEFT : CommandTrait.Hand.SHIFT_RIGHT) ? handIn == CommandTrait.Hand.LEFT ? CommandTrait.Hand.SHIFT_LEFT : CommandTrait.Hand.SHIFT_RIGHT
: handIn; : handIn;
NPCCommandDispatchEvent event = new NPCCommandDispatchEvent(npc, player); NPCCommandDispatchEvent event = new NPCCommandDispatchEvent(npc, player);
Bukkit.getServer().getPluginManager().callEvent(event); Bukkit.getServer().getPluginManager().callEvent(event);
@ -266,9 +265,8 @@ public class CommandTrait extends Trait {
@Override @Override
public void run() { public void run() {
List<NPCCommand> commandList = Lists.newArrayList(Iterables.filter(commands.values(), command -> { List<NPCCommand> commandList = Lists.newArrayList(Iterables.filter(commands.values(),
return command.hand == hand || command.hand == Hand.BOTH; command -> (command.hand == hand || command.hand == Hand.BOTH)));
}));
if (executionMode == ExecutionMode.RANDOM) { if (executionMode == ExecutionMode.RANDOM) {
if (commandList.size() > 0) { if (commandList.size() > 0) {
runCommand(player, commandList.get(Util.getFastRandom().nextInt(commandList.size()))); runCommand(player, commandList.get(Util.getFastRandom().nextInt(commandList.size())));
@ -277,7 +275,7 @@ public class CommandTrait extends Trait {
} }
int max = -1; int max = -1;
if (executionMode == ExecutionMode.SEQUENTIAL) { if (executionMode == ExecutionMode.SEQUENTIAL) {
Collections.sort(commandList, (o1, o2) -> Integer.compare(o1.id, o2.id)); Collections.sort(commandList, Comparator.comparing(o1 -> o1.id));
max = commandList.size() > 0 ? commandList.get(commandList.size() - 1).id : -1; max = commandList.size() > 0 ? commandList.get(commandList.size() - 1).id : -1;
} }
if (executionMode == ExecutionMode.LINEAR) { if (executionMode == ExecutionMode.LINEAR) {
@ -299,13 +297,13 @@ public class CommandTrait extends Trait {
} }
} }
runCommand(player, command); runCommand(player, command);
if (executionMode == ExecutionMode.SEQUENTIAL || (charged != null && charged == false)) { if (executionMode == ExecutionMode.SEQUENTIAL || charged != null && !charged) {
break; break;
} }
} }
} }
private void runCommand(final Player player, NPCCommand command) { private void runCommand(Player player, NPCCommand command) {
Runnable runnable = () -> { Runnable runnable = () -> {
PlayerNPCCommand info = playerTracking.get(player.getUniqueId()); PlayerNPCCommand info = playerTracking.get(player.getUniqueId());
if (info == null && (executionMode == ExecutionMode.SEQUENTIAL if (info == null && (executionMode == ExecutionMode.SEQUENTIAL
@ -320,14 +318,12 @@ public class CommandTrait extends Trait {
return; return;
} }
} }
if (info != null && !info.canUse(CommandTrait.this, player, command)) if (info != null && !info.canUse(CommandTrait.this, player, command))
return; return;
if (charged == null) { if (charged == null) {
charge.run(); charge.run();
} }
if (temporaryPermissions.size() > 0) { if (temporaryPermissions.size() > 0) {
PermissionAttachment attachment = player.addAttachment(CitizensAPI.getPlugin()); PermissionAttachment attachment = player.addAttachment(CitizensAPI.getPlugin());
if (attachment != null) { if (attachment != null) {
@ -434,9 +430,8 @@ public class CommandTrait extends Trait {
private void sendErrorMessage(Player player, CommandTraitError msg, Function<String, String> transform, private void sendErrorMessage(Player player, CommandTraitError msg, Function<String, String> transform,
Object... objects) { Object... objects) {
if (hideErrorMessages) { if (hideErrorMessages)
return; return;
}
Set<CommandTraitError> sent = executionErrors.get(player.getUniqueId().toString()); Set<CommandTraitError> sent = executionErrors.get(player.getUniqueId().toString());
if (sent != null) { if (sent != null) {
if (sent.contains(msg)) if (sent.contains(msg))
@ -465,7 +460,7 @@ public class CommandTrait extends Trait {
} }
public void setExecutionMode(ExecutionMode mode) { public void setExecutionMode(ExecutionMode mode) {
this.executionMode = mode; executionMode = mode;
} }
public void setExperienceCost(int experienceCost) { public void setExperienceCost(int experienceCost) {
@ -477,7 +472,7 @@ public class CommandTrait extends Trait {
} }
public void setHideErrorMessages(boolean hide) { public void setHideErrorMessages(boolean hide) {
this.hideErrorMessages = hide; hideErrorMessages = hide;
} }
public void setItemCost(List<ItemStack> itemCost, int id) { public void setItemCost(List<ItemStack> itemCost, int id) {
@ -550,7 +545,7 @@ public class CommandTrait extends Trait {
@Override @Override
public void initialise(MenuContext ctx) { public void initialise(MenuContext ctx) {
this.inventory = ctx.getInventory(); inventory = ctx.getInventory();
if (id == -1) { if (id == -1) {
for (ItemStack stack : trait.itemRequirements) { for (ItemStack stack : trait.itemRequirements) {
inventory.addItem(stack.clone()); inventory.addItem(stack.clone());
@ -576,10 +571,10 @@ public class CommandTrait extends Trait {
} }
} }
if (id == -1) { if (id == -1) {
this.trait.itemRequirements.clear(); trait.itemRequirements.clear();
this.trait.itemRequirements.addAll(requirements); trait.itemRequirements.addAll(requirements);
} else { } else {
this.trait.setItemCost(requirements, id); trait.setItemCost(requirements, id);
} }
} }
} }
@ -649,7 +644,7 @@ public class CommandTrait extends Trait {
} }
public NPCCommandBuilder addPerm(String permission) { public NPCCommandBuilder addPerm(String permission) {
this.perms.add(permission); perms.add(permission);
return this; return this;
} }
@ -697,7 +692,7 @@ public class CommandTrait extends Trait {
} }
public NPCCommandBuilder globalCooldown(int cooldown) { public NPCCommandBuilder globalCooldown(int cooldown) {
this.globalCooldown = cooldown; globalCooldown = cooldown;
return this; return this;
} }
@ -739,8 +734,8 @@ public class CommandTrait extends Trait {
double cost = root.keyExists("cost") ? root.getDouble("cost") : -1; double cost = root.keyExists("cost") ? root.getDouble("cost") : -1;
int exp = root.keyExists("experienceCost") ? root.getInt("experienceCost") : -1; int exp = root.keyExists("experienceCost") ? root.getInt("experienceCost") : -1;
return new NPCCommand(Integer.parseInt(root.name()), root.getString("command"), return new NPCCommand(Integer.parseInt(root.name()), root.getString("command"),
Hand.valueOf(root.getString("hand")), Boolean.valueOf(root.getString("player")), Hand.valueOf(root.getString("hand")), Boolean.parseBoolean(root.getString("player")),
Boolean.valueOf(root.getString("op")), root.getInt("cooldown"), perms, root.getInt("n"), Boolean.parseBoolean(root.getString("op")), root.getInt("cooldown"), perms, root.getInt("n"),
root.getInt("delay"), root.getInt("globalcooldown"), cost, exp, items); root.getInt("delay"), root.getInt("globalcooldown"), cost, exp, items);
} }
@ -848,14 +843,12 @@ public class CommandTrait extends Trait {
} }
} }
} }
Set<String> diff = Sets.newHashSet(lastUsed.keySet()); Set<String> diff = Sets.newHashSet(lastUsed.keySet());
diff.removeAll(commandKeys); diff.removeAll(commandKeys);
for (String key : diff) { for (String key : diff) {
lastUsed.remove(key); lastUsed.remove(key);
nUsed.remove(key); nUsed.remove(key);
} }
if (globalCooldowns != null) { if (globalCooldowns != null) {
diff = Sets.newHashSet(globalCooldowns.keySet()); diff = Sets.newHashSet(globalCooldowns.keySet());
diff.removeAll(commandKeys); diff.removeAll(commandKeys);
@ -867,7 +860,7 @@ public class CommandTrait extends Trait {
public static boolean requiresTracking(NPCCommand command) { public static boolean requiresTracking(NPCCommand command) {
return command.globalCooldown > 0 || command.cooldown > 0 || command.n > 0 return command.globalCooldown > 0 || command.cooldown > 0 || command.n > 0
|| (command.perms != null && command.perms.size() > 0) || command.perms != null && command.perms.size() > 0
|| Setting.NPC_COMMAND_GLOBAL_COMMAND_COOLDOWN.asSeconds() > 0; || Setting.NPC_COMMAND_GLOBAL_COMMAND_COOLDOWN.asSeconds() > 0;
} }
} }

View File

@ -74,7 +74,6 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
} else if (args.hasValueFlag("explicittype")) { } else if (args.hasValueFlag("explicittype")) {
explicitType = Util.matchEnum(EntityType.values(), args.getFlag("explicittype")); explicitType = Util.matchEnum(EntityType.values(), args.getFlag("explicittype"));
} }
if (npc.isSpawned()) { if (npc.isSpawned()) {
loadController(); loadController();
} }
@ -90,10 +89,8 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
} }
if (!player.hasPermission( if (!player.hasPermission(
"citizens.npc.controllable." + npc.getEntity().getType().name().toLowerCase().replace("_", "")) "citizens.npc.controllable." + npc.getEntity().getType().name().toLowerCase().replace("_", ""))
|| !player.hasPermission("citizens.npc.controllable")) || !player.hasPermission("citizens.npc.controllable")
return; || ownerRequired && !npc.getOrAddTrait(Owner.class).isOwnedBy(player))
if (ownerRequired && !npc.getOrAddTrait(Owner.class).isOwnedBy(player))
return; return;
NMS.mount(npc.getEntity(), player); NMS.mount(npc.getEntity(), player);
@ -146,9 +143,8 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
*/ */
public boolean mount(Player toMount) { public boolean mount(Player toMount) {
List<Entity> passengers = NMS.getPassengers(npc.getEntity()); List<Entity> passengers = NMS.getPassengers(npc.getEntity());
if (passengers.size() != 0) { if (passengers.size() != 0)
return false; return false;
}
boolean found = false; boolean found = false;
for (Entity passenger : passengers) { for (Entity passenger : passengers) {
if (passenger != null && passenger == toMount) { if (passenger != null && passenger == toMount) {
@ -156,9 +152,8 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
break; break;
} }
} }
if (found) { if (found)
return false; return false;
}
enterOrLeaveVehicle(toMount); enterOrLeaveVehicle(toMount);
return true; return true;
} }
@ -232,7 +227,7 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
* the explicit type * the explicit type
*/ */
public void setExplicitType(EntityType type) { public void setExplicitType(EntityType type) {
this.explicitType = type; explicitType = type;
} }
private void setMountedYaw(Entity entity) { private void setMountedYaw(Entity entity) {
@ -240,9 +235,8 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
return; // EnderDragon handles this separately return; // EnderDragon handles this separately
Location loc = entity.getLocation(); Location loc = entity.getLocation();
Vector vel = entity.getVelocity(); Vector vel = entity.getVelocity();
if (vel.lengthSquared() == 0) { if (vel.lengthSquared() == 0)
return; return;
}
double tX = loc.getX() + vel.getX(); double tX = loc.getX() + vel.getX();
double tZ = loc.getZ() + vel.getZ(); double tZ = loc.getZ() + vel.getZ();
@ -287,7 +281,6 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
vel = vel.setX(dXcos * speed * speedMod).setZ(dXsin * speed * speedMod); vel = vel.setX(dXcos * speed * speedMod).setZ(dXsin * speed * speedMod);
} }
vel = vel.add(new Vector( vel = vel.add(new Vector(
passenger.getVelocity().getX() * speedMod * Setting.CONTROLLABLE_GROUND_DIRECTION_MODIFIER.asDouble(), passenger.getVelocity().getX() * speedMod * Setting.CONTROLLABLE_GROUND_DIRECTION_MODIFIER.asDouble(),
0D, 0D,
@ -301,11 +294,10 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
} }
handle.setVelocity(vel); handle.setVelocity(vel);
if (newSpeed > oldSpeed && speed < maxSpeed) { if (newSpeed > oldSpeed && speed < maxSpeed)
return (float) Math.min(maxSpeed, (speed + ((maxSpeed - speed) / 50.0D))); return (float) Math.min(maxSpeed, speed + (maxSpeed - speed) / 50.0D);
} else { else
return (float) Math.max(0, (speed - (speed / 50.0D))); return (float) Math.max(0, speed - speed / 50.0D);
}
} }
public class GroundController implements MovementController { public class GroundController implements MovementController {
@ -329,13 +321,12 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
public void run(Player rider) { public void run(Player rider) {
boolean onGround = NMS.isOnGround(npc.getEntity()); boolean onGround = NMS.isOnGround(npc.getEntity());
float speedMod = npc.getNavigator().getDefaultParameters() float speedMod = npc.getNavigator().getDefaultParameters()
.modifiedSpeed((onGround ? GROUND_SPEED : AIR_SPEED)); .modifiedSpeed(onGround ? GROUND_SPEED : AIR_SPEED);
if (!Util.isHorse(npc.getEntity().getType())) { if (!Util.isHorse(npc.getEntity().getType())) {
// use minecraft horse physics // use minecraft horse physics
speed = updateHorizontalSpeed(npc.getEntity(), rider, speed, speedMod, speed = updateHorizontalSpeed(npc.getEntity(), rider, speed, speedMod,
Setting.MAX_CONTROLLABLE_GROUND_SPEED.asDouble()); Setting.MAX_CONTROLLABLE_GROUND_SPEED.asDouble());
} }
boolean shouldJump = NMS.shouldJump(rider); boolean shouldJump = NMS.shouldJump(rider);
if (shouldJump) { if (shouldJump) {
if (onGround && jumpTicks == 0) { if (onGround && jumpTicks == 0) {
@ -420,14 +411,12 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
npc.getEntity().setVelocity(npc.getEntity().getVelocity().setY(0.001F)); npc.getEntity().setVelocity(npc.getEntity().getVelocity().setY(0.001F));
return; return;
} }
speed = updateHorizontalSpeed(npc.getEntity(), rider, speed, 1F, speed = updateHorizontalSpeed(npc.getEntity(), rider, speed, 1F,
Setting.MAX_CONTROLLABLE_FLIGHT_SPEED.asDouble()); Setting.MAX_CONTROLLABLE_FLIGHT_SPEED.asDouble());
boolean shouldJump = NMS.shouldJump(rider); boolean shouldJump = NMS.shouldJump(rider);
if (shouldJump) { if (shouldJump) {
npc.getEntity().setVelocity(npc.getEntity().getVelocity().setY(0.25F)); npc.getEntity().setVelocity(npc.getEntity().getVelocity().setY(0.25F));
} }
npc.getEntity().setVelocity(npc.getEntity().getVelocity().multiply(new Vector(1, 0.98, 1))); npc.getEntity().setVelocity(npc.getEntity().getVelocity().multiply(new Vector(1, 0.98, 1)));
setMountedYaw(npc.getEntity()); setMountedYaw(npc.getEntity());
} }
@ -461,7 +450,7 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
} }
} }
private static final Map<EntityType, Constructor<? extends MovementController>> CONTROLLER_TYPES = Maps private static Map<EntityType, Constructor<? extends MovementController>> CONTROLLER_TYPES = Maps
.newEnumMap(EntityType.class); .newEnumMap(EntityType.class);
static { static {

View File

@ -51,7 +51,7 @@ public class CurrentLocation extends Trait {
} }
public void setLocation(Location loc) { public void setLocation(Location loc) {
this.location = loc.clone(); location = loc.clone();
} }
@Override @Override

View File

@ -70,7 +70,7 @@ public class DropsTrait extends Trait {
@Override @Override
public void initialise(MenuContext ctx) { public void initialise(MenuContext ctx) {
this.inventory = ctx.getInventory(); inventory = ctx.getInventory();
int k = 0; int k = 0;
for (int i = 1; i < 5; i += 2) { for (int i = 1; i < 5; i += 2) {
for (int j = 0; j < 9; j++) { for (int j = 0; j < 9; j++) {
@ -87,7 +87,7 @@ public class DropsTrait extends Trait {
InventoryMenuSlot slot = ctx.getSlot(i * 9 + j); InventoryMenuSlot slot = ctx.getSlot(i * 9 + j);
slot.setItemStack(new ItemStack(Util.getFallbackMaterial("BARRIER", "FIRE")), slot.setItemStack(new ItemStack(Util.getFallbackMaterial("BARRIER", "FIRE")),
"Drop chance <e>" + chance + "%"); "Drop chance <e>" + chance + "%");
slot.setClickHandler(new PercentageSlotHandler((pct) -> { slot.setClickHandler(new PercentageSlotHandler(pct -> {
if (chances.containsKey(islot)) { if (chances.containsKey(islot)) {
chances.put(islot, pct / 100.0); chances.put(islot, pct / 100.0);
} }
@ -116,12 +116,13 @@ public class DropsTrait extends Trait {
for (int j = 0; j < 9; j++) { for (int j = 0; j < 9; j++) {
int slot = i * 9 + j; int slot = i * 9 + j;
ItemStack stack = inventory.getItem(slot); ItemStack stack = inventory.getItem(slot);
if (stack == null || stack.getType() == Material.AIR) if (stack == null || stack.getType() == Material.AIR) {
continue; continue;
}
drops.add(new ItemDrop(stack.clone(), chances.getOrDefault(slot, 1.0))); drops.add(new ItemDrop(stack.clone(), chances.getOrDefault(slot, 1.0)));
} }
} }
this.trait.drops = drops; trait.drops = drops;
} }
} }

View File

@ -40,7 +40,7 @@ public class FollowTrait extends Trait {
* Sets the {@link Entity} to follow * Sets the {@link Entity} to follow
*/ */
public void follow(Entity entity) { public void follow(Entity entity) {
this.followingUUID = entity == null ? null : entity.getUniqueId(); followingUUID = entity == null ? null : entity.getUniqueId();
if (npc.getNavigator().isNavigating() && this.entity != null && npc.getNavigator().getEntityTarget() != null if (npc.getNavigator().isNavigating() && this.entity != null && npc.getNavigator().getEntityTarget() != null
&& this.entity == npc.getNavigator().getEntityTarget().getTarget()) { && this.entity == npc.getNavigator().getEntityTarget().getTarget()) {
npc.getNavigator().cancelNavigation(); npc.getNavigator().cancelNavigation();
@ -112,13 +112,11 @@ public class FollowTrait extends Trait {
} }
return; return;
} }
if (!npc.getNavigator().isNavigating()) { if (!npc.getNavigator().isNavigating()) {
npc.getNavigator().setTarget(entity, false); npc.getNavigator().setTarget(entity, false);
if (margin > 0) { if (margin > 0) {
npc.getNavigator().getLocalParameters().distanceMargin(margin); npc.getNavigator().getLocalParameters().distanceMargin(margin);
} }
} else { } else {
flock.run(); flock.run();
} }

View File

@ -51,7 +51,7 @@ public class Gravity extends Trait implements Toggleable {
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
this.nogravity = enabled; nogravity = enabled;
} }
@Override @Override

View File

@ -35,7 +35,6 @@ import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName; import net.citizensnpcs.api.trait.TraitName;
import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.Placeholders; import net.citizensnpcs.api.util.Placeholders;
import net.citizensnpcs.api.util.SpigotUtil; import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.util.NMS; import net.citizensnpcs.util.NMS;
@ -59,6 +58,8 @@ public class HologramTrait extends Trait {
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore()); private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
private int t; private int t;
private boolean useDisplayEntities = Setting.DISPLAY_ENTITY_HOLOGRAMS.asBoolean(); private boolean useDisplayEntities = Setting.DISPLAY_ENTITY_HOLOGRAMS.asBoolean();
@Persist
private int viewRange = -1;
public HologramTrait() { public HologramTrait() {
super("hologramtrait"); super("hologramtrait");
@ -109,12 +110,13 @@ public class HologramTrait extends Trait {
hologramNPC = registry.createNPC(EntityType.ARMOR_STAND, line); hologramNPC = registry.createNPC(EntityType.ARMOR_STAND, line);
hologramNPC.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc); hologramNPC.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc);
} }
if (Setting.PACKET_HOLOGRAMS.asBoolean()) { if (Setting.PACKET_HOLOGRAMS.asBoolean()) {
hologramNPC.addTrait(PacketNPC.class); hologramNPC.addTrait(PacketNPC.class);
} }
hologramNPC.data().set(NPC.Metadata.HOLOGRAM_FOR, npc.getUniqueId().toString()); hologramNPC.data().set(NPC.Metadata.HOLOGRAM_FOR, npc.getUniqueId().toString());
if (viewRange != -1) {
hologramNPC.data().set(NPC.Metadata.TRACKING_RANGE, viewRange);
}
hologramNPC.spawn(currentLoc.clone().add(0, getEntityHeight() + heightOffset, 0)); hologramNPC.spawn(currentLoc.clone().add(0, getEntityHeight() + heightOffset, 0));
Matcher itemMatcher = ITEM_MATCHER.matcher(line); Matcher itemMatcher = ITEM_MATCHER.matcher(line);
@ -122,7 +124,7 @@ public class HologramTrait extends Trait {
Material item = SpigotUtil.isUsing1_13API() ? Material.matchMaterial(itemMatcher.group(1), false) Material item = SpigotUtil.isUsing1_13API() ? Material.matchMaterial(itemMatcher.group(1), false)
: Material.matchMaterial(itemMatcher.group(1)); : Material.matchMaterial(itemMatcher.group(1));
ItemStack itemStack = new ItemStack(item, 1); ItemStack itemStack = new ItemStack(item, 1);
final NPC itemNPC = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", itemStack); NPC itemNPC = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", itemStack);
itemNPC.data().setPersistent(NPC.Metadata.NAMEPLATE_VISIBLE, false); itemNPC.data().setPersistent(NPC.Metadata.NAMEPLATE_VISIBLE, false);
if (itemMatcher.group(2) != null) { if (itemMatcher.group(2) != null) {
if (itemMatcher.group(2).charAt(1) == '{') { if (itemMatcher.group(2).charAt(1) == '{') {
@ -135,14 +137,13 @@ public class HologramTrait extends Trait {
} }
itemNPC.getOrAddTrait(MountTrait.class).setMountedOn(hologramNPC.getUniqueId()); itemNPC.getOrAddTrait(MountTrait.class).setMountedOn(hologramNPC.getUniqueId());
itemNPC.spawn(currentLoc); itemNPC.spawn(currentLoc);
final NPC hn = hologramNPC; NPC hn = hologramNPC;
itemNPC.addRunnable(() -> { itemNPC.addRunnable(() -> {
if (!itemNPC.isSpawned() || !hn.isSpawned()) { if (!itemNPC.isSpawned() || !hn.isSpawned()) {
itemNPC.destroy(); itemNPC.destroy();
} }
}); });
} }
lastEntityHeight = getEntityHeight(); lastEntityHeight = getEntityHeight();
return hologramNPC; return hologramNPC;
} }
@ -152,7 +153,7 @@ public class HologramTrait extends Trait {
} }
private double getHeight(int lineNumber) { private double getHeight(int lineNumber) {
double base = (lastNameplateVisible ? 0 : -getLineHeight()); double base = lastNameplateVisible ? 0 : -getLineHeight();
for (int i = 0; i <= lineNumber; i++) { for (int i = 0; i <= lineNumber; i++) {
HologramLine line = lines.get(i); HologramLine line = lines.get(i);
base += line.mb + getLineHeight(); base += line.mb + getLineHeight();
@ -192,6 +193,17 @@ public class HologramTrait extends Trait {
return nameLine != null && nameLine.hologram.isSpawned() ? nameLine.hologram.getEntity() : null; return nameLine != null && nameLine.hologram.isSpawned() ? nameLine.hologram.getEntity() : null;
} }
public double getViewRange() {
return viewRange;
}
public boolean isHologramSneaking(NPC hologram, Player player) {
if (nameLine != null && hologram == nameLine.hologram && npc.getEntity() instanceof Player
&& ((Player) npc.getEntity()).isSneaking())
return true;
return false;
}
@Override @Override
public void load(DataKey root) { public void load(DataKey root) {
clear(); clear();
@ -210,7 +222,6 @@ public class HologramTrait extends Trait {
nameLine.removeNPC(); nameLine.removeNPC();
nameLine = null; nameLine = null;
} }
for (HologramLine line : lines) { for (HologramLine line : lines) {
line.removeNPC(); line.removeNPC();
} }
@ -229,11 +240,9 @@ public class HologramTrait extends Trait {
} }
} }
} }
if (height == -1) if (height == -1)
return; return;
Messaging.debug(npc, "hologram interaction ", hologram.getEntity(), "height offset set to", height);
NMS.linkTextInteraction(player, hologram.getEntity(), npc.getEntity(), height); NMS.linkTextInteraction(player, hologram.getEntity(), npc.getEntity(), height);
} }
} }
@ -255,7 +264,6 @@ public class HologramTrait extends Trait {
nameLine = new HologramLine(npc.getRawName(), false); nameLine = new HologramLine(npc.getRawName(), false);
nameLine.spawnNPC(0); nameLine.spawnNPC(0);
} }
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
lines.get(i).spawnNPC(getHeight(i)); lines.get(i).spawnNPC(getHeight(i));
} }
@ -265,7 +273,6 @@ public class HologramTrait extends Trait {
for (HologramLine line : lines) { for (HologramLine line : lines) {
line.removeNPC(); line.removeNPC();
} }
if (!npc.isSpawned()) if (!npc.isSpawned())
return; return;
@ -294,11 +301,9 @@ public class HologramTrait extends Trait {
onDespawn(); onDespawn();
return; return;
} }
if (currentLoc == null) { if (currentLoc == null) {
currentLoc = npc.getStoredLocation().clone(); currentLoc = npc.getStoredLocation().clone();
} }
boolean nameplateVisible = Boolean boolean nameplateVisible = Boolean
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString()); .parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
if (npc.requiresNameHologram()) { if (npc.requiresNameHologram()) {
@ -310,7 +315,6 @@ public class HologramTrait extends Trait {
nameLine.spawnNPC(0); nameLine.spawnNPC(0);
} }
} }
Location npcLoc = npc.getStoredLocation(); Location npcLoc = npc.getStoredLocation();
boolean updatePosition = Setting.HOLOGRAM_ALWAYS_UPDATE_POSITION.asBoolean() boolean updatePosition = Setting.HOLOGRAM_ALWAYS_UPDATE_POSITION.asBoolean()
|| currentLoc.getWorld() != npcLoc.getWorld() || currentLoc.distance(npcLoc) >= 0.001 || currentLoc.getWorld() != npcLoc.getWorld() || currentLoc.distance(npcLoc) >= 0.001
@ -321,59 +325,50 @@ public class HologramTrait extends Trait {
t = 0; t = 0;
updateName = true; updateName = true;
} }
lastNameplateVisible = nameplateVisible; lastNameplateVisible = nameplateVisible;
if (updatePosition) { if (updatePosition) {
currentLoc = npcLoc.clone(); currentLoc = npcLoc.clone();
lastEntityHeight = getEntityHeight(); lastEntityHeight = getEntityHeight();
} }
if (nameLine != null && nameLine.hologram.isSpawned()) { if (nameLine != null && nameLine.hologram.isSpawned()) {
if (updatePosition && !useDisplayEntities) { if (updatePosition && !useDisplayEntities) {
nameLine.hologram.teleport(npcLoc.clone().add(0, getEntityHeight(), 0), TeleportCause.PLUGIN); nameLine.hologram.teleport(npcLoc.clone().add(0, getEntityHeight(), 0), TeleportCause.PLUGIN);
} }
if (updateName) { if (updateName) {
nameLine.setText(npc.getRawName()); nameLine.setText(npc.getRawName());
} }
if (useDisplayEntities && nameLine.hologram.getEntity().getVehicle() == null) { if (useDisplayEntities && nameLine.hologram.getEntity().getVehicle() == null) {
npc.getEntity().addPassenger(nameLine.hologram.getEntity()); npc.getEntity().addPassenger(nameLine.hologram.getEntity());
} }
} }
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
HologramLine line = lines.get(i); HologramLine line = lines.get(i);
NPC hologramNPC = line.hologram; NPC hologramNPC = line.hologram;
if (hologramNPC == null || !hologramNPC.isSpawned()) if (hologramNPC == null || !hologramNPC.isSpawned()) {
continue; continue;
}
if (line.ticks > 0 && --line.ticks == 0) { if (line.ticks > 0 && --line.ticks == 0) {
line.removeNPC(); line.removeNPC();
lines.remove(i--); lines.remove(i--);
continue; continue;
} }
if (updatePosition && !useDisplayEntities) { if (updatePosition && !useDisplayEntities) {
Location tp = npcLoc.clone().add(0, lastEntityHeight + getHeight(i), 0); Location tp = npcLoc.clone().add(0, lastEntityHeight + getHeight(i), 0);
hologramNPC.teleport(tp, TeleportCause.PLUGIN); hologramNPC.teleport(tp, TeleportCause.PLUGIN);
} }
if (useDisplayEntities && hologramNPC.getEntity().getVehicle() == null) { if (useDisplayEntities && hologramNPC.getEntity().getVehicle() == null) {
npc.getEntity().addPassenger(hologramNPC.getEntity()); npc.getEntity().addPassenger(hologramNPC.getEntity());
} }
String text = line.text; String text = line.text;
if (ITEM_MATCHER.matcher(text).matches()) { if (ITEM_MATCHER.matcher(text).matches()) {
hologramNPC.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, false); hologramNPC.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, false);
continue; continue;
} }
if (!updateName) {
if (!updateName)
continue; continue;
}
line.setText(text); line.setText(text);
} }
} }
@ -383,8 +378,9 @@ public class HologramTrait extends Trait {
root.removeKey("lines"); root.removeKey("lines");
int i = 0; int i = 0;
for (HologramLine line : lines) { for (HologramLine line : lines) {
if (!line.persist) if (!line.persist) {
continue; continue;
}
root.setString("lines." + i + ".text", line.text); root.setString("lines." + i + ".text", line.text);
root.setDouble("lines." + i + ".margin.top", line.mt); root.setDouble("lines." + i + ".margin.top", line.mt);
root.setDouble("lines." + i + ".margin.bottom", line.mb); root.setDouble("lines." + i + ".margin.bottom", line.mb);
@ -405,7 +401,6 @@ public class HologramTrait extends Trait {
addLine(text); addLine(text);
return; return;
} }
HologramLine line = lines.get(idx); HologramLine line = lines.get(idx);
line.setText(text); line.setText(text);
if (line.hologram == null) { if (line.hologram == null) {
@ -441,7 +436,6 @@ public class HologramTrait extends Trait {
} else if (type.equalsIgnoreCase("bottom")) { } else if (type.equalsIgnoreCase("bottom")) {
lines.get(idx).mb = margin; lines.get(idx).mb = margin;
} }
reloadLineHolograms(); reloadLineHolograms();
} }
@ -449,11 +443,16 @@ public class HologramTrait extends Trait {
* Implementation-specific method: {@see NPC.Metadata#HOLOGRAM_LINE_SUPPLIER} * Implementation-specific method: {@see NPC.Metadata#HOLOGRAM_LINE_SUPPLIER}
*/ */
public void setPerPlayerTextSupplier(BiFunction<String, Player, String> nameSupplier) { public void setPerPlayerTextSupplier(BiFunction<String, Player, String> nameSupplier) {
this.customHologramSupplier = nameSupplier; customHologramSupplier = nameSupplier;
} }
public void setUseDisplayEntities(boolean use) { public void setUseDisplayEntities(boolean use) {
this.useDisplayEntities = use; useDisplayEntities = use;
reloadLineHolograms();
}
public void setViewRange(int range) {
this.viewRange = range;
reloadLineHolograms(); reloadLineHolograms();
} }
@ -508,7 +507,7 @@ public class HologramTrait extends Trait {
public void spawnNPC(double height) { public void spawnNPC(double height) {
String name = Placeholders.replace(text, null, npc); String name = Placeholders.replace(text, null, npc);
this.hologram = createHologram(name, height); hologram = createHologram(name, height);
if (customHologramSupplier != null) { if (customHologramSupplier != null) {
hologram.data().set(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER, hologram.data().set(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER,
(Function<Player, String>) p -> customHologramSupplier.apply(text, p)); (Function<Player, String>) p -> customHologramSupplier.apply(text, p));
@ -529,8 +528,8 @@ public class HologramTrait extends Trait {
return Collections.emptyList(); return Collections.emptyList();
} }
private static final List<String> LINE_ARGS = ImmutableList.of("set", "remove", "margintop", "marginbottom"); private static List<String> LINE_ARGS = ImmutableList.of("set", "remove", "margintop", "marginbottom");
} }
private static final Pattern ITEM_MATCHER = Pattern.compile("<item:(.*?)([:].*?)?>"); private static Pattern ITEM_MATCHER = Pattern.compile("<item:(.*?)([:].*?)?>");
} }

View File

@ -125,7 +125,6 @@ public class LookClose extends Trait implements Toggleable {
} }
sessions.clear(); sessions.clear();
} }
if (lookingAt != null && !isValid(lookingAt)) { if (lookingAt != null && !isValid(lookingAt)) {
NPCLookCloseChangeTargetEvent event = new NPCLookCloseChangeTargetEvent(npc, lookingAt, null); NPCLookCloseChangeTargetEvent event = new NPCLookCloseChangeTargetEvent(npc, lookingAt, null);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
@ -135,7 +134,6 @@ public class LookClose extends Trait implements Toggleable {
lookingAt = null; lookingAt = null;
} }
} }
Player old = lookingAt; Player old = lookingAt;
if (lookingAt != null) { if (lookingAt != null) {
if (randomSwitchTargets && t <= 0) { if (randomSwitchTargets && t <= 0) {
@ -150,19 +148,18 @@ public class LookClose extends Trait implements Toggleable {
Location npcLoc = npc.getStoredLocation(); Location npcLoc = npc.getStoredLocation();
for (Player player : getNearbyPlayers()) { for (Player player : getNearbyPlayers()) {
double dist = player.getLocation().distance(npcLoc); double dist = player.getLocation().distance(npcLoc);
if (dist > min) if (dist > min) {
continue; continue;
}
min = dist; min = dist;
lookingAt = player; lookingAt = player;
} }
} }
if (old != lookingAt) { if (old != lookingAt) {
NPCLookCloseChangeTargetEvent event = new NPCLookCloseChangeTargetEvent(npc, old, lookingAt); NPCLookCloseChangeTargetEvent event = new NPCLookCloseChangeTargetEvent(npc, old, lookingAt);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
if (lookingAt != event.getNewTarget() && event.getNewTarget() != null && !isValid(event.getNewTarget())) { if (lookingAt != event.getNewTarget() && event.getNewTarget() != null && !isValid(event.getNewTarget()))
return; return;
}
lookingAt = event.getNewTarget(); lookingAt = event.getNewTarget();
} }
} }
@ -176,11 +173,12 @@ public class LookClose extends Trait implements Toggleable {
.map(e -> (Player) e).collect(Collectors.toList()) .map(e -> (Player) e).collect(Collectors.toList())
: CitizensAPI.getLocationLookup().getNearbyPlayers(npcLoc, range); : CitizensAPI.getLocationLookup().getNearbyPlayers(npcLoc, range);
for (Player player : nearby) { for (Player player : nearby) {
if (player == lookingAt || (!targetNPCs && CitizensAPI.getNPCRegistry().getNPC(player) != null)) if (player == lookingAt || !targetNPCs && CitizensAPI.getNPCRegistry().getNPC(player) != null) {
continue; continue;
if (player.getLocation().getWorld() != npcLoc.getWorld() || isInvisible(player)) }
if (player.getLocation().getWorld() != npcLoc.getWorld() || isInvisible(player)) {
continue; continue;
}
options.add(player); options.add(player);
} }
return options; return options;
@ -221,9 +219,8 @@ public class LookClose extends Trait implements Toggleable {
private boolean isPluginVanished(Player player) { private boolean isPluginVanished(Player player) {
for (MetadataValue meta : player.getMetadata("vanished")) { for (MetadataValue meta : player.getMetadata("vanished")) {
if (meta.asBoolean()) { if (meta.asBoolean())
return true; return true;
}
} }
return false; return false;
} }
@ -274,7 +271,6 @@ public class LookClose extends Trait implements Toggleable {
lookingAt = null; lookingAt = null;
return; return;
} }
if (enableRandomLook) { if (enableRandomLook) {
if (!npc.getNavigator().isNavigating() && lookingAt == null && t <= 0) { if (!npc.getNavigator().isNavigating() && lookingAt == null && t <= 0) {
randomLook(); randomLook();
@ -283,22 +279,15 @@ public class LookClose extends Trait implements Toggleable {
} }
t--; t--;
if (!enabled) { if (!enabled || npc.getNavigator().isNavigating() && disableWhileNavigating()) {
lookingAt = null; lookingAt = null;
return; return;
} }
if (npc.getNavigator().isNavigating() && disableWhileNavigating()) {
lookingAt = null;
return;
}
findNewTarget(); findNewTarget();
if (npc.getNavigator().isNavigating() || npc.getNavigator().isPaused()) { if (npc.getNavigator().isNavigating() || npc.getNavigator().isPaused()) {
npc.getNavigator().setPaused(lookingAt != null); npc.getNavigator().setPaused(lookingAt != null);
} }
if (lookingAt == null) if (lookingAt == null)
return; return;
@ -348,15 +337,15 @@ public class LookClose extends Trait implements Toggleable {
* Sets the delay between random looking in ticks * Sets the delay between random looking in ticks
*/ */
public void setRandomLookDelay(int delay) { public void setRandomLookDelay(int delay) {
this.randomLookDelay = delay; randomLookDelay = delay;
} }
public void setRandomLookPitchRange(float min, float max) { public void setRandomLookPitchRange(float min, float max) {
this.randomPitchRange = new float[] { min, max }; randomPitchRange = new float[] { min, max };
} }
public void setRandomLookYawRange(float min, float max) { public void setRandomLookYawRange(float min, float max) {
this.randomYawRange = new float[] { min, max }; randomYawRange = new float[] { min, max };
} }
public void setRandomlySwitchTargets(boolean randomSwitchTargets) { public void setRandomlySwitchTargets(boolean randomSwitchTargets) {
@ -374,11 +363,11 @@ public class LookClose extends Trait implements Toggleable {
* Enables/disables realistic looking (using line of sight checks). More computationally expensive. * Enables/disables realistic looking (using line of sight checks). More computationally expensive.
*/ */
public void setRealisticLooking(boolean realistic) { public void setRealisticLooking(boolean realistic) {
this.realisticLooking = realistic; realisticLooking = realistic;
} }
public void setTargetNPCs(boolean target) { public void setTargetNPCs(boolean target) {
this.targetNPCs = target; targetNPCs = target;
} }
public boolean targetNPCs() { public boolean targetNPCs() {

View File

@ -35,7 +35,7 @@ public class OcelotModifiers extends Trait {
} }
public void setSitting(boolean sit) { public void setSitting(boolean sit) {
this.sitting = sit; sitting = sit;
updateModifiers(); updateModifiers();
} }

View File

@ -64,9 +64,8 @@ public class PacketNPC extends Trait {
} }
public EntityController wrap(EntityController controller) { public EntityController wrap(EntityController controller) {
if (!(controller instanceof PacketController)) { if (!(controller instanceof PacketController))
return new PacketController(controller); return new PacketController(controller);
}
return controller; return controller;
} }
@ -74,7 +73,7 @@ public class PacketNPC extends Trait {
private final EntityController base; private final EntityController base;
public PacketController(EntityController controller) { public PacketController(EntityController controller) {
this.base = controller; base = controller;
} }
@Override @Override

View File

@ -43,9 +43,7 @@ public class PausePathfindingTrait extends Trait {
@Override @Override
public void run() { public void run() {
if (playerRange == -1 || !npc.isSpawned()) if (playerRange == -1 || !npc.isSpawned() || unpauseTaskId == -1 && !npc.getNavigator().isNavigating())
return;
if (unpauseTaskId == -1 && !npc.getNavigator().isNavigating())
return; return;
if (CitizensAPI.getLocationLookup().getNearbyPlayers(npc.getStoredLocation(), playerRange).iterator() if (CitizensAPI.getLocationLookup().getNearbyPlayers(npc.getStoredLocation(), playerRange).iterator()
.hasNext()) { .hasNext()) {
@ -58,7 +56,7 @@ public class PausePathfindingTrait extends Trait {
} }
public void setPlayerRangeBlocks(double range) { public void setPlayerRangeBlocks(double range) {
this.playerRange = range; playerRange = range;
} }
public void setRightClick(boolean rightclick) { public void setRightClick(boolean rightclick) {

View File

@ -93,10 +93,8 @@ public class Poses extends Trait {
paginator.addLine(line); paginator.addLine(line);
i++; i++;
} }
if (!paginator.sendPage(sender, page))
if (!paginator.sendPage(sender, page)) {
throw new CommandException(Messages.COMMAND_PAGE_MISSING, page); throw new CommandException(Messages.COMMAND_PAGE_MISSING, page);
}
} }
public Pose getPose(String name) { public Pose getPose(String name) {
@ -115,7 +113,8 @@ public class Poses extends Trait {
for (DataKey sub : key.getRelative("list").getIntegerSubKeys()) { for (DataKey sub : key.getRelative("list").getIntegerSubKeys()) {
try { try {
String[] parts = sub.getString("").split(";"); String[] parts = sub.getString("").split(";");
poses.put(parts[0].toLowerCase(), new Pose(parts[0], Float.valueOf(parts[1]), Float.valueOf(parts[2]))); poses.put(parts[0].toLowerCase(),
new Pose(parts[0], Float.parseFloat(parts[1]), Float.parseFloat(parts[2])));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Messaging.logTr(Messages.SKIPPING_INVALID_POSE, sub.name(), e.getMessage()); Messaging.logTr(Messages.SKIPPING_INVALID_POSE, sub.name(), e.getMessage());
} }
@ -149,6 +148,6 @@ public class Poses extends Trait {
} }
public void setDefaultPose(String pose) { public void setDefaultPose(String pose) {
this.defaultPose = pose; defaultPose = pose;
} }
} }

View File

@ -20,6 +20,10 @@ public class Powered extends Trait implements Toggleable {
super("powered"); super("powered");
} }
public boolean isPowered() {
return powered;
}
@Override @Override
public void onSpawn() { public void onSpawn() {
if (npc.getEntity() instanceof Creeper) { if (npc.getEntity() instanceof Creeper) {
@ -27,10 +31,6 @@ public class Powered extends Trait implements Toggleable {
} }
} }
public boolean isPowered() {
return powered;
}
@Override @Override
public boolean toggle() { public boolean toggle() {
powered = !powered; powered = !powered;

View File

@ -60,7 +60,6 @@ public class RotationTrait extends Trait {
} else { } else {
packetSessions.add(lrs); packetSessions.add(lrs);
} }
return lrs; return lrs;
} }
@ -82,11 +81,9 @@ public class RotationTrait extends Trait {
return lrs; return lrs;
for (PacketRotationSession session : packetSessions) { for (PacketRotationSession session : packetSessions) {
if (session.accepts(player) && session.triple != null) { if (session.accepts(player) && session.triple != null)
return session; return session;
}
} }
return null; return null;
} }
@ -102,31 +99,28 @@ public class RotationTrait extends Trait {
if (npc.data().get(NPC.Metadata.RESET_PITCH_ON_TICK, false)) { if (npc.data().get(NPC.Metadata.RESET_PITCH_ON_TICK, false)) {
NMS.setPitch(npc.getEntity(), 0); NMS.setPitch(npc.getEntity(), 0);
} }
Set<PacketRotationSession> ran = Sets.newHashSet(); Set<PacketRotationSession> ran = Sets.newHashSet();
for (Iterator<PacketRotationSession> itr = Iterables.concat(packetSessions, packetSessionsByUUID.values()) for (Iterator<PacketRotationSession> itr = Iterables.concat(packetSessions, packetSessionsByUUID.values())
.iterator(); itr.hasNext();) { .iterator(); itr.hasNext();) {
PacketRotationSession session = itr.next(); PacketRotationSession session = itr.next();
if (ran.contains(session)) if (ran.contains(session)) {
continue; continue;
}
ran.add(session); ran.add(session);
session.run(npc.getEntity()); session.run(npc.getEntity());
if (!session.isActive()) { if (!session.isActive()) {
itr.remove(); itr.remove();
} }
} }
if (npc.getNavigator().isNavigating())
if (npc.getNavigator().isNavigating()) {
// npc.yHeadRot = rotateIfNecessary(npc.yHeadRot, npc.yBodyRot, 75); // npc.yHeadRot = rotateIfNecessary(npc.yHeadRot, npc.yBodyRot, 75);
return; return;
}
globalSession.run(new EntityRotation(npc.getEntity())); globalSession.run(new EntityRotation(npc.getEntity()));
} }
private static class EntityRotation extends RotationTriple { private static class EntityRotation extends RotationTriple {
protected final Entity entity; protected Entity entity;
public EntityRotation(Entity entity) { public EntityRotation(Entity entity) {
super(NMS.getYaw(entity), NMS.getHeadYaw(entity), entity.getLocation().getPitch()); super(NMS.getYaw(entity), NMS.getHeadYaw(entity), entity.getLocation().getPitch());
@ -158,7 +152,7 @@ public class RotationTrait extends Trait {
} }
public void end() { public void end() {
this.ended = true; ended = true;
} }
public float getBodyYaw() { public float getBodyYaw() {
@ -193,7 +187,6 @@ public class RotationTrait extends Trait {
if (triple == null) { if (triple == null) {
triple = new PacketRotationTriple(entity); triple = new PacketRotationTriple(entity);
} }
session.run(triple); session.run(triple);
if (!session.isActive()) { if (!session.isActive()) {
triple = null; triple = null;
@ -265,7 +258,7 @@ public class RotationTrait extends Trait {
} }
public RotationParams linkedBody(boolean linked) { public RotationParams linkedBody(boolean linked) {
this.linkedBody = linked; linkedBody = linked;
return this; return this;
} }
@ -297,12 +290,12 @@ public class RotationTrait extends Trait {
} }
public RotationParams maxPitchPerTick(float val) { public RotationParams maxPitchPerTick(float val) {
this.maxPitchPerTick = val; maxPitchPerTick = val;
return this; return this;
} }
public RotationParams maxYawPerTick(float val) { public RotationParams maxYawPerTick(float val) {
this.maxYawPerTick = val; maxYawPerTick = val;
return this; return this;
} }
@ -312,7 +305,7 @@ public class RotationTrait extends Trait {
} }
public RotationParams pitchRange(float[] val) { public RotationParams pitchRange(float[] val) {
this.pitchRange = val; pitchRange = val;
return this; return this;
} }
@ -343,29 +336,24 @@ public class RotationTrait extends Trait {
if (headOnly) { if (headOnly) {
key.setBoolean("headOnly", headOnly); key.setBoolean("headOnly", headOnly);
} }
if (immediate) { if (immediate) {
key.setBoolean("immediate", immediate); key.setBoolean("immediate", immediate);
} }
if (maxPitchPerTick != 10) { if (maxPitchPerTick != 10) {
key.setDouble("maxPitchPerTick", maxPitchPerTick); key.setDouble("maxPitchPerTick", maxPitchPerTick);
} else { } else {
key.removeKey("maxPitchPerTick"); key.removeKey("maxPitchPerTick");
} }
if (maxYawPerTick != 40) { if (maxYawPerTick != 40) {
key.setDouble("maxYawPerTick", maxYawPerTick); key.setDouble("maxYawPerTick", maxYawPerTick);
} else { } else {
key.removeKey("maxYawPerTick"); key.removeKey("maxYawPerTick");
} }
if (pitchRange[0] != -180 || pitchRange[1] != 180) { if (pitchRange[0] != -180 || pitchRange[1] != 180) {
key.setString("pitchRange", pitchRange[0] + "," + pitchRange[1]); key.setString("pitchRange", pitchRange[0] + "," + pitchRange[1]);
} else { } else {
key.removeKey("pitchRange"); key.removeKey("pitchRange");
} }
if (yawRange[0] != -180 || yawRange[1] != 180) { if (yawRange[0] != -180 || yawRange[1] != 180) {
key.setString("yawRange", yawRange[0] + "," + yawRange[1]); key.setString("yawRange", yawRange[0] + "," + yawRange[1]);
} else { } else {
@ -374,7 +362,7 @@ public class RotationTrait extends Trait {
} }
public RotationParams uuidFilter(List<UUID> uuids) { public RotationParams uuidFilter(List<UUID> uuids) {
this.uuidFilter = uuids; uuidFilter = uuids;
return this; return this;
} }
@ -383,7 +371,7 @@ public class RotationTrait extends Trait {
} }
public RotationParams yawRange(float[] val) { public RotationParams yawRange(float[] val) {
this.yawRange = val; yawRange = val;
return this; return this;
} }
} }
@ -489,21 +477,18 @@ public class RotationTrait extends Trait {
rot.bodyYaw = Math.abs(body - lo) > Math.abs(body - hi) ? hi : lo; rot.bodyYaw = Math.abs(body - lo) > Math.abs(body - hi) ? hi : lo;
} }
} }
rot.pitch = params.immediate ? getTargetPitch() : params.rotatePitchTowards(t, rot.pitch, getTargetPitch()); rot.pitch = params.immediate ? getTargetPitch() : params.rotatePitchTowards(t, rot.pitch, getTargetPitch());
t++; t++;
if (params.linkedBody) { if (params.linkedBody) {
rot.bodyYaw = rot.headYaw; rot.bodyYaw = rot.headYaw;
} }
if (Math.abs(rot.pitch - getTargetPitch()) + Math.abs(rot.headYaw - getTargetYaw()) < 0.1) { if (Math.abs(rot.pitch - getTargetPitch()) + Math.abs(rot.headYaw - getTargetYaw()) < 0.1) {
t = -1; t = -1;
if (!params.headOnly) { if (!params.headOnly) {
rot.bodyYaw = rot.headYaw; rot.bodyYaw = rot.headYaw;
} }
} }
rot.apply(); rot.apply();
} }
} }

View File

@ -35,18 +35,20 @@ public class ScoreboardTrait extends Trait {
private final PerPlayerMetadata<Boolean> metadata; private final PerPlayerMetadata<Boolean> metadata;
private ChatColor previousGlowingColor; private ChatColor previousGlowingColor;
@Persist @Persist
private Set<String> tags = new HashSet<String>(); private Set<String> tags = new HashSet<>();
public ScoreboardTrait() { public ScoreboardTrait() {
super("scoreboardtrait"); super("scoreboardtrait");
metadata = CitizensAPI.getLocationLookup().<Boolean> registerMetadata("scoreboard", (meta, event) -> { metadata = CitizensAPI.getLocationLookup().<Boolean> registerMetadata("scoreboard", (meta, event) -> {
for (NPC npc : CitizensAPI.getNPCRegistry()) { for (NPC npc : CitizensAPI.getNPCRegistry()) {
ScoreboardTrait trait = npc.getTraitNullable(ScoreboardTrait.class); ScoreboardTrait trait = npc.getTraitNullable(ScoreboardTrait.class);
if (trait == null) if (trait == null) {
continue; continue;
}
Team team = trait.getTeam(); Team team = trait.getTeam();
if (team == null || meta.has(event.getPlayer().getUniqueId(), team.getName())) if (team == null || meta.has(event.getPlayer().getUniqueId(), team.getName())) {
continue; continue;
}
NMS.sendTeamPacket(event.getPlayer(), team, 0); NMS.sendTeamPacket(event.getPlayer(), team, 0);
meta.set(event.getPlayer().getUniqueId(), team.getName(), true); meta.set(event.getPlayer().getUniqueId(), team.getName(), true);
} }
@ -161,13 +163,11 @@ public class ScoreboardTrait extends Trait {
npc.data().remove(NPC.Metadata.SCOREBOARD_FAKE_TEAM_NAME); npc.data().remove(NPC.Metadata.SCOREBOARD_FAKE_TEAM_NAME);
return; return;
} }
if (npc.isSpawned()) { if (npc.isSpawned()) {
lastName = npc.getEntity() instanceof Player && npc.getEntity().getName() != null lastName = npc.getEntity() instanceof Player && npc.getEntity().getName() != null
? npc.getEntity().getName() ? npc.getEntity().getName()
: npc.getUniqueId().toString(); : npc.getUniqueId().toString();
} }
if (SUPPORT_TAGS) { if (SUPPORT_TAGS) {
try { try {
if (!npc.getEntity().getScoreboardTags().equals(tags)) { if (!npc.getEntity().getScoreboardTags().equals(tags)) {
@ -177,7 +177,6 @@ public class ScoreboardTrait extends Trait {
SUPPORT_TAGS = false; SUPPORT_TAGS = false;
} }
} }
if (SUPPORT_TEAM_SETOPTION) { if (SUPPORT_TEAM_SETOPTION) {
try { try {
OptionStatus visibility = nameVisibility ? OptionStatus.ALWAYS : OptionStatus.NEVER; OptionStatus visibility = nameVisibility ? OptionStatus.ALWAYS : OptionStatus.NEVER;
@ -193,7 +192,6 @@ public class ScoreboardTrait extends Trait {
} else { } else {
NMS.setTeamNameTagVisible(team, nameVisibility); NMS.setTeamNameTagVisible(team, nameVisibility);
} }
if (SUPPORT_COLLIDABLE_SETOPTION) { if (SUPPORT_COLLIDABLE_SETOPTION) {
try { try {
OptionStatus collide = npc.data().<Boolean> get(NPC.Metadata.COLLIDABLE, !npc.isProtected()) OptionStatus collide = npc.data().<Boolean> get(NPC.Metadata.COLLIDABLE, !npc.isProtected())
@ -209,7 +207,6 @@ public class ScoreboardTrait extends Trait {
SUPPORT_COLLIDABLE_SETOPTION = false; SUPPORT_COLLIDABLE_SETOPTION = false;
} }
} }
if (color != null) { if (color != null) {
if (SUPPORT_GLOWING_COLOR && SpigotUtil.getMinecraftPackage().contains("1_12_R1")) { if (SUPPORT_GLOWING_COLOR && SpigotUtil.getMinecraftPackage().contains("1_12_R1")) {
SUPPORT_GLOWING_COLOR = false; SUPPORT_GLOWING_COLOR = false;
@ -217,7 +214,7 @@ public class ScoreboardTrait extends Trait {
if (SUPPORT_GLOWING_COLOR) { if (SUPPORT_GLOWING_COLOR) {
try { try {
if (team.getColor() == null || previousGlowingColor == null if (team.getColor() == null || previousGlowingColor == null
|| (previousGlowingColor != null && color != previousGlowingColor)) { || previousGlowingColor != null && color != previousGlowingColor) {
team.setColor(color); team.setColor(color);
previousGlowingColor = color; previousGlowingColor = color;
changed = true; changed = true;
@ -225,22 +222,19 @@ public class ScoreboardTrait extends Trait {
} catch (NoSuchMethodError err) { } catch (NoSuchMethodError err) {
SUPPORT_GLOWING_COLOR = false; SUPPORT_GLOWING_COLOR = false;
} }
} else { } else if (team.getPrefix() == null || team.getPrefix().length() == 0 || previousGlowingColor == null
if (team.getPrefix() == null || team.getPrefix().length() == 0 || previousGlowingColor == null || previousGlowingColor != null && !team.getPrefix().equals(previousGlowingColor.toString())) {
|| (previousGlowingColor != null team.setPrefix(color.toString());
&& !team.getPrefix().equals(previousGlowingColor.toString()))) { previousGlowingColor = color;
team.setPrefix(color.toString()); changed = true;
previousGlowingColor = color;
changed = true;
}
} }
} }
if (!changed) if (!changed)
return; return;
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (player.hasMetadata("NPC")) if (player.hasMetadata("NPC")) {
continue; continue;
}
if (metadata.has(player.getUniqueId(), team.getName())) { if (metadata.has(player.getUniqueId(), team.getName())) {
NMS.sendTeamPacket(player, team, 2); NMS.sendTeamPacket(player, team, 2);
} else { } else {

View File

@ -34,7 +34,7 @@ public class SheepTrait extends Trait {
@EventHandler @EventHandler
private void onPlayerShearEntityEvent(PlayerShearEntityEvent event) { private void onPlayerShearEntityEvent(PlayerShearEntityEvent event) {
if (npc != null && npc.equals(CitizensAPI.getNPCRegistry().getNPC(event.getEntity()))) { if (npc != null && npc.isProtected() && npc.equals(CitizensAPI.getNPCRegistry().getNPC(event.getEntity()))) {
event.setCancelled(true); event.setCancelled(true);
} }
} }

View File

@ -35,7 +35,6 @@ public class SitTrait extends Trait {
((Sittable) npc.getEntity()).setSitting(false); ((Sittable) npc.getEntity()).setSitting(false);
return; return;
} }
if (chair != null) { if (chair != null) {
if (chair.getEntity() != null) { if (chair.getEntity() != null) {
chair.getEntity().eject(); chair.getEntity().eject();
@ -72,7 +71,6 @@ public class SitTrait extends Trait {
} }
return; return;
} }
if (chair == null) { if (chair == null) {
NPCRegistry registry = CitizensAPI.getNamedNPCRegistry("SitRegistry"); NPCRegistry registry = CitizensAPI.getNamedNPCRegistry("SitRegistry");
if (registry == null) { if (registry == null) {
@ -85,22 +83,19 @@ public class SitTrait extends Trait {
return; return;
} }
} }
if (chair.isSpawned() && !NMS.getPassengers(chair.getEntity()).contains(npc.getEntity())) { if (chair.isSpawned() && !NMS.getPassengers(chair.getEntity()).contains(npc.getEntity())) {
NMS.mount(chair.getEntity(), npc.getEntity()); NMS.mount(chair.getEntity(), npc.getEntity());
} }
if (chair.getStoredLocation() != null && chair.getStoredLocation().distance(sittingAt) >= 0.03) { if (chair.getStoredLocation() != null && chair.getStoredLocation().distance(sittingAt) >= 0.03) {
chair.teleport(sittingAt.clone(), TeleportCause.PLUGIN); chair.teleport(sittingAt.clone(), TeleportCause.PLUGIN);
} }
} }
public void setSitting(Location at) { public void setSitting(Location at) {
this.sittingAt = at != null ? at.clone() : null; sittingAt = at != null ? at.clone() : null;
if (requiresPassengerOffsetCorrection()) { if (requiresPassengerOffsetCorrection()) {
sittingAt = sittingAt.add(0, 0.3, 0); sittingAt = sittingAt.add(0, 0.3, 0);
} }
if (at == null) { if (at == null) {
onDespawn(); onDespawn();
} }

View File

@ -258,10 +258,10 @@ public class SkinLayers extends Trait {
RIGHT_PANTS(5), RIGHT_PANTS(5),
RIGHT_SLEEVE(3); RIGHT_SLEEVE(3);
final int flag; int flag;
Layer(int offset) { Layer(int offset) {
this.flag = 1 << offset; flag = 1 << offset;
} }
public static byte toByte(Set<Layer> flags) { public static byte toByte(Set<Layer> flags) {

View File

@ -109,14 +109,14 @@ public class SkinTrait extends Trait {
* @see #fetchDefaultSkin * @see #fetchDefaultSkin
*/ */
public void setFetchDefaultSkin(boolean fetch) { public void setFetchDefaultSkin(boolean fetch) {
this.fetchDefaultSkin = fetch; fetchDefaultSkin = fetch;
} }
/** /**
* @see #shouldUpdateSkins() * @see #shouldUpdateSkins()
*/ */
public void setShouldUpdateSkins(boolean update) { public void setShouldUpdateSkins(boolean update) {
this.updateSkins = update; updateSkins = update;
} }
/** /**
@ -170,14 +170,14 @@ public class SkinTrait extends Trait {
throw new IllegalArgumentException("Invalid texture data"); throw new IllegalArgumentException("Invalid texture data");
this.signature = signature; this.signature = signature;
this.textureRaw = data; textureRaw = data;
this.updateSkins = false; updateSkins = false;
npc.data().setPersistent(Skin.CACHED_SKIN_UUID_NAME_METADATA, skinName.toLowerCase()); npc.data().setPersistent(Skin.CACHED_SKIN_UUID_NAME_METADATA, skinName.toLowerCase());
onSkinChange(false); onSkinChange(false);
} }
public void setTexture(String value, String signature) { public void setTexture(String value, String signature) {
this.textureRaw = value; textureRaw = value;
this.signature = signature; this.signature = signature;
} }

View File

@ -36,7 +36,6 @@ public class SleepTrait extends Trait {
} }
return; return;
} }
if (SUPPORT_BLOCKDATA == null) { if (SUPPORT_BLOCKDATA == null) {
try { try {
SUPPORT_BLOCKDATA = true; SUPPORT_BLOCKDATA = true;
@ -45,14 +44,13 @@ public class SleepTrait extends Trait {
SUPPORT_BLOCKDATA = false; SUPPORT_BLOCKDATA = false;
} }
} }
if (npc.getEntity() instanceof Player) { if (npc.getEntity() instanceof Player) {
Player player = (Player) npc.getEntity(); Player player = (Player) npc.getEntity();
if (!SUPPORT_BLOCKSTATE) { if (!SUPPORT_BLOCKSTATE) {
NMS.sleep(player, true); NMS.sleep(player, true);
} else { } else {
try { try {
if ((SUPPORT_BLOCKDATA && at.getBlock().getBlockData() instanceof Bed) if (SUPPORT_BLOCKDATA && at.getBlock().getBlockData() instanceof Bed
|| at.getBlock().getState() instanceof Bed) { || at.getBlock().getState() instanceof Bed) {
player.sleep(at, true); player.sleep(at, true);
} else { } else {

View File

@ -39,7 +39,7 @@ public class SneakTrait extends Trait {
} }
public void setSneaking(boolean sneak) { public void setSneaking(boolean sneak) {
this.sneaking = sneak; sneaking = sneak;
apply(); apply();
} }
} }

View File

@ -54,7 +54,7 @@ public class WolfModifiers extends Trait {
} }
public void setCollarColor(DyeColor color) { public void setCollarColor(DyeColor color) {
this.collarColor = color; collarColor = color;
updateModifiers(); updateModifiers();
} }

View File

@ -98,7 +98,7 @@ public class CommandAction extends NPCShopAction {
@Override @Override
public void initialise(MenuContext ctx) { public void initialise(MenuContext ctx) {
for (int i = 0; i < 3 * 9; i++) { for (int i = 0; i < 3 * 9; i++) {
final int idx = i; int idx = i;
ctx.getSlot(i).clear(); ctx.getSlot(i).clear();
if (i < base.commands.size()) { if (i < base.commands.size()) {
ctx.getSlot(i).setItemStack(new ItemStack(Material.FEATHER), "<f>Set command", ctx.getSlot(i).setItemStack(new ItemStack(Material.FEATHER), "<f>Set command",
@ -114,7 +114,7 @@ public class CommandAction extends NPCShopAction {
return; return;
} }
ctx.getMenu().transition(InputMenus ctx.getMenu().transition(InputMenus
.stringSetter(() -> idx < base.commands.size() ? base.commands.get(idx) : "", (res) -> { .stringSetter(() -> idx < base.commands.size() ? base.commands.get(idx) : "", res -> {
if (res == null) { if (res == null) {
if (idx < base.commands.size()) { if (idx < base.commands.size()) {
base.commands.remove(idx); base.commands.remove(idx);
@ -131,11 +131,11 @@ public class CommandAction extends NPCShopAction {
} }
ctx.getSlot(3 * 9 + 3).setItemStack(new ItemStack(Util.getFallbackMaterial("COMMAND_BLOCK", "COMMAND")), ctx.getSlot(3 * 9 + 3).setItemStack(new ItemStack(Util.getFallbackMaterial("COMMAND_BLOCK", "COMMAND")),
"Run commands as server", base.server ? ChatColor.GREEN + "On" : ChatColor.RED + "OFF"); "Run commands as server", base.server ? ChatColor.GREEN + "On" : ChatColor.RED + "OFF");
ctx.getSlot(3 * 9 + 3).addClickHandler(InputMenus.toggler((res) -> base.server = res, base.server)); ctx.getSlot(3 * 9 + 3).addClickHandler(InputMenus.toggler(res -> base.server = res, base.server));
ctx.getSlot(3 * 9 + 4).setItemStack( ctx.getSlot(3 * 9 + 4).setItemStack(
new ItemStack(Util.getFallbackMaterial("COMPARATOR", "REDSTONE_COMPARATOR")), "Run commands as op", new ItemStack(Util.getFallbackMaterial("COMPARATOR", "REDSTONE_COMPARATOR")), "Run commands as op",
base.op ? ChatColor.GREEN + "On" : ChatColor.RED + "OFF"); base.op ? ChatColor.GREEN + "On" : ChatColor.RED + "OFF");
ctx.getSlot(3 * 9 + 4).addClickHandler(InputMenus.clickToggle((res) -> { ctx.getSlot(3 * 9 + 4).addClickHandler(InputMenus.clickToggle(res -> {
base.op = res; base.op = res;
return res ? ChatColor.GREEN + "On" : ChatColor.RED + "Off"; return res ? ChatColor.GREEN + "On" : ChatColor.RED + "Off";
}, base.server)); }, base.server));

View File

@ -20,7 +20,7 @@ public class ExperienceAction extends NPCShopAction {
} }
public ExperienceAction(int cost) { public ExperienceAction(int cost) {
this.exp = cost; exp = cost;
} }
@Override @Override
@ -32,6 +32,7 @@ public class ExperienceAction extends NPCShopAction {
public int getMaxRepeats(Entity entity) { public int getMaxRepeats(Entity entity) {
if (!(entity instanceof Player)) if (!(entity instanceof Player))
return 0; return 0;
return ((Player) entity).getLevel() / exp; return ((Player) entity).getLevel() / exp;
} }
@ -39,11 +40,10 @@ public class ExperienceAction extends NPCShopAction {
public Transaction grant(Entity entity, int repeats) { public Transaction grant(Entity entity, int repeats) {
if (!(entity instanceof Player)) if (!(entity instanceof Player))
return Transaction.fail(); return Transaction.fail();
Player player = (Player) entity; Player player = (Player) entity;
int amount = exp * repeats; int amount = exp * repeats;
return Transaction.create(() -> { return Transaction.create(() -> true, () -> {
return true;
}, () -> {
player.setLevel(player.getLevel() + amount); player.setLevel(player.getLevel() + amount);
}, () -> { }, () -> {
player.setLevel(player.getLevel() - amount); player.setLevel(player.getLevel() - amount);
@ -54,11 +54,10 @@ public class ExperienceAction extends NPCShopAction {
public Transaction take(Entity entity, int repeats) { public Transaction take(Entity entity, int repeats) {
if (!(entity instanceof Player)) if (!(entity instanceof Player))
return Transaction.fail(); return Transaction.fail();
Player player = (Player) entity; Player player = (Player) entity;
int amount = exp * repeats; int amount = exp * repeats;
return Transaction.create(() -> { return Transaction.create(() -> (player.getLevel() >= amount), () -> {
return player.getLevel() >= amount;
}, () -> {
player.setLevel(player.getLevel() - amount); player.setLevel(player.getLevel() - amount);
}, () -> { }, () -> {
player.setLevel(player.getLevel() + amount); player.setLevel(player.getLevel() + amount);
@ -68,12 +67,13 @@ public class ExperienceAction extends NPCShopAction {
public static class ExperienceActionGUI implements GUI { public static class ExperienceActionGUI implements GUI {
@Override @Override
public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback) { public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback) {
final ExperienceAction action = previous == null ? new ExperienceAction() : (ExperienceAction) previous; ExperienceAction action = previous == null ? new ExperienceAction() : (ExperienceAction) previous;
return InputMenus.filteredStringSetter(() -> Integer.toString(action.exp), s -> { return InputMenus.filteredStringSetter(() -> Integer.toString(action.exp), s -> {
try { try {
int result = Integer.parseInt(s); int result = Integer.parseInt(s);
if (result < 0) if (result < 0)
return false; return false;
action.exp = result; action.exp = result;
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
return false; return false;

View File

@ -56,18 +56,18 @@ public class ItemAction extends NPCShopAction {
ItemStack[] contents = source.getContents(); ItemStack[] contents = source.getContents();
for (int i = 0; i < contents.length; i++) { for (int i = 0; i < contents.length; i++) {
ItemStack toMatch = contents[i]; ItemStack toMatch = contents[i];
if (toMatch == null || toMatch.getType() == Material.AIR) if (toMatch == null || toMatch.getType() == Material.AIR || tooDamaged(toMatch)) {
continue;
if (tooDamaged(toMatch))
continue; continue;
}
toMatch = toMatch.clone(); toMatch = toMatch.clone();
for (int j = 0; j < items.size(); j++) { for (int j = 0; j < items.size(); j++) {
if (toMatch == null) if (toMatch == null) {
break; break;
}
ItemStack item = items.get(j); ItemStack item = items.get(j);
if (req.get(j) <= 0 || !matches(item, toMatch)) if (req.get(j) <= 0 || !matches(item, toMatch)) {
continue; continue;
}
int remaining = req.get(j); int remaining = req.get(j);
int taken = toMatch.getAmount() > remaining ? remaining : toMatch.getAmount(); int taken = toMatch.getAmount() > remaining ? remaining : toMatch.getAmount();
@ -76,7 +76,6 @@ public class ItemAction extends NPCShopAction {
} else { } else {
toMatch.setAmount(toMatch.getAmount() - taken); toMatch.setAmount(toMatch.getAmount() - taken);
} }
if (modify) { if (modify) {
if (toMatch == null) { if (toMatch == null) {
source.clear(i); source.clear(i);
@ -92,9 +91,8 @@ public class ItemAction extends NPCShopAction {
@Override @Override
public String describe() { public String describe() {
if (items.size() == 1) { if (items.size() == 1)
return items.get(0).getAmount() + " " + Util.prettyEnum(items.get(0).getType()); return items.get(0).getAmount() + " " + Util.prettyEnum(items.get(0).getType());
}
String description = items.size() + " items"; String description = items.size() + " items";
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
ItemStack item = items.get(i); ItemStack item = items.get(i);
@ -113,24 +111,25 @@ public class ItemAction extends NPCShopAction {
return 0; return 0;
Inventory source = ((InventoryHolder) entity).getInventory(); Inventory source = ((InventoryHolder) entity).getInventory();
List<Integer> req = items.stream().map(i -> i.getAmount()).collect(Collectors.toList()); List<Integer> req = items.stream().map(ItemStack::getAmount).collect(Collectors.toList());
List<Integer> has = items.stream().map(i -> 0).collect(Collectors.toList()); List<Integer> has = items.stream().map(i -> 0).collect(Collectors.toList());
ItemStack[] contents = source.getContents(); ItemStack[] contents = source.getContents();
for (int i = 0; i < contents.length; i++) { for (int i = 0; i < contents.length; i++) {
ItemStack toMatch = contents[i]; ItemStack toMatch = contents[i];
if (toMatch == null || toMatch.getType() == Material.AIR) if (toMatch == null || toMatch.getType() == Material.AIR || tooDamaged(toMatch))
continue;
if (tooDamaged(toMatch))
continue; continue;
toMatch = toMatch.clone(); toMatch = toMatch.clone();
for (int j = 0; j < items.size(); j++) { for (int j = 0; j < items.size(); j++) {
if (!matches(items.get(j), toMatch)) if (!matches(items.get(j), toMatch))
continue; continue;
int remaining = req.get(j); int remaining = req.get(j);
int taken = toMatch.getAmount() > remaining ? remaining : toMatch.getAmount(); int taken = toMatch.getAmount() > remaining ? remaining : toMatch.getAmount();
has.set(j, has.get(j) + taken); has.set(j, has.get(j) + taken);
if (toMatch.getAmount() - taken <= 0) if (toMatch.getAmount() - taken <= 0) {
break; break;
}
toMatch.setAmount(toMatch.getAmount() - taken); toMatch.setAmount(toMatch.getAmount() - taken);
} }
} }
@ -164,12 +163,12 @@ public class ItemAction extends NPCShopAction {
} }
private boolean matches(ItemStack a, ItemStack b) { private boolean matches(ItemStack a, ItemStack b) {
if (a.getType() != b.getType()) if (a.getType() != b.getType() || metaFilter.size() > 0 && !metaMatches(a, b, metaFilter))
return false;
if (metaFilter.size() > 0 && !metaMatches(a, b, metaFilter))
return false; return false;
if (compareSimilarity && !a.isSimilar(b)) if (compareSimilarity && !a.isSimilar(b))
return false; return false;
return true; return true;
} }
@ -181,22 +180,20 @@ public class ItemAction extends NPCShopAction {
Tag acc = source; Tag acc = source;
Tag cmp = compare; Tag cmp = compare;
for (int i = 0; i < parts.length; i++) { for (int i = 0; i < parts.length; i++) {
if (acc == null) if (acc == null || cmp == null)
return false;
if (cmp == null)
return false; return false;
if (i < parts.length - 1) { if (i < parts.length - 1) {
if (!(acc instanceof CompoundTag) || !(cmp instanceof CompoundTag)) if (!(acc instanceof CompoundTag) || !(cmp instanceof CompoundTag))
return false; return false;
if (parts[i].equals(acc.getName()) && acc.getName().equals(cmp.getName())) if (parts[i].equals(acc.getName()) && acc.getName().equals(cmp.getName())) {
continue; continue;
}
acc = ((CompoundTag) acc).getValue().get(parts[i]); acc = ((CompoundTag) acc).getValue().get(parts[i]);
cmp = ((CompoundTag) cmp).getValue().get(parts[i]); cmp = ((CompoundTag) cmp).getValue().get(parts[i]);
continue; continue;
} }
if (!acc.getName().equals(parts[i]) || !cmp.getName().equals(parts[i])) if (!acc.getName().equals(parts[i]) || !cmp.getName().equals(parts[i])
return false; || !acc.getValue().equals(cmp.getValue()))
if (!acc.getValue().equals(cmp.getValue()))
return false; return false;
} }
} }
@ -207,10 +204,9 @@ public class ItemAction extends NPCShopAction {
public Transaction take(Entity entity, int repeats) { public Transaction take(Entity entity, int repeats) {
if (!(entity instanceof InventoryHolder)) if (!(entity instanceof InventoryHolder))
return Transaction.fail(); return Transaction.fail();
Inventory source = ((InventoryHolder) entity).getInventory(); Inventory source = ((InventoryHolder) entity).getInventory();
return Transaction.create(() -> { return Transaction.create(() -> containsItems(source, repeats, false), () -> {
return containsItems(source, repeats, false);
}, () -> {
containsItems(source, repeats, true); containsItems(source, repeats, true);
}, () -> { }, () -> {
source.addItem(items.stream().map(ItemStack::clone).toArray(ItemStack[]::new)); source.addItem(items.stream().map(ItemStack::clone).toArray(ItemStack[]::new));
@ -220,8 +216,10 @@ public class ItemAction extends NPCShopAction {
private boolean tooDamaged(ItemStack toMatch) { private boolean tooDamaged(ItemStack toMatch) {
if (!requireUndamaged) if (!requireUndamaged)
return false; return false;
if (SpigotUtil.isUsing1_13API()) if (SpigotUtil.isUsing1_13API())
return toMatch.getItemMeta() instanceof Damageable && ((Damageable) toMatch.getItemMeta()).getDamage() != 0; return toMatch.getItemMeta() instanceof Damageable && ((Damageable) toMatch.getItemMeta()).getDamage() != 0;
return toMatch.getDurability() == toMatch.getType().getMaxDurability(); return toMatch.getDurability() == toMatch.getType().getMaxDurability();
} }
@ -253,20 +251,19 @@ public class ItemAction extends NPCShopAction {
event.setCancelled(true); event.setCancelled(true);
}); });
} }
ctx.getSlot(3 * 9 + 1).setItemStack(new ItemStack(Material.ANVIL), "Must have no damage", ctx.getSlot(3 * 9 + 1).setItemStack(new ItemStack(Material.ANVIL), "Must have no damage",
base.requireUndamaged ? ChatColor.GREEN + "On" : ChatColor.RED + "Off"); base.requireUndamaged ? ChatColor.GREEN + "On" : ChatColor.RED + "Off");
ctx.getSlot(3 * 9 + 1) ctx.getSlot(3 * 9 + 1)
.addClickHandler(InputMenus.toggler((res) -> base.requireUndamaged = res, base.requireUndamaged)); .addClickHandler(InputMenus.toggler(res -> base.requireUndamaged = res, base.requireUndamaged));
ctx.getSlot(3 * 9 + 2).setItemStack( ctx.getSlot(3 * 9 + 2).setItemStack(
new ItemStack(Util.getFallbackMaterial("COMPARATOR", "REDSTONE_COMPARATOR")), new ItemStack(Util.getFallbackMaterial("COMPARATOR", "REDSTONE_COMPARATOR")),
"Compare item similarity", base.compareSimilarity ? ChatColor.GREEN + "On" : ChatColor.RED + "Off"); "Compare item similarity", base.compareSimilarity ? ChatColor.GREEN + "On" : ChatColor.RED + "Off");
ctx.getSlot(3 * 9 + 2) ctx.getSlot(3 * 9 + 2)
.addClickHandler(InputMenus.toggler((res) -> base.compareSimilarity = res, base.compareSimilarity)); .addClickHandler(InputMenus.toggler(res -> base.compareSimilarity = res, base.compareSimilarity));
ctx.getSlot(3 * 9 + 3).setItemStack(new ItemStack(Material.BOOK), "NBT comparison filter", ctx.getSlot(3 * 9 + 3).setItemStack(new ItemStack(Material.BOOK), "NBT comparison filter",
Joiner.on("\n").join(base.metaFilter)); Joiner.on("\n").join(base.metaFilter));
ctx.getSlot(3 * 9 + 3) ctx.getSlot(3 * 9 + 3)
.addClickHandler((event) -> ctx.getMenu() .addClickHandler(event -> ctx.getMenu()
.transition(InputMenus.stringSetter(() -> Joiner.on(',').join(base.metaFilter), .transition(InputMenus.stringSetter(() -> Joiner.on(',').join(base.metaFilter),
res -> base.metaFilter = res == null ? null : Arrays.asList(res.split(","))))); res -> base.metaFilter = res == null ? null : Arrays.asList(res.split(",")))));
} }

View File

@ -22,7 +22,7 @@ public class MoneyAction extends NPCShopAction {
} }
public MoneyAction(double cost) { public MoneyAction(double cost) {
this.money = cost; money = cost;
} }
@Override @Override
@ -35,6 +35,7 @@ public class MoneyAction extends NPCShopAction {
public int getMaxRepeats(Entity entity) { public int getMaxRepeats(Entity entity) {
if (!(entity instanceof Player)) if (!(entity instanceof Player))
return 0; return 0;
Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider(); Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider();
return (int) Math.floor(economy.getBalance((Player) entity) / money); return (int) Math.floor(economy.getBalance((Player) entity) / money);
} }
@ -43,12 +44,12 @@ public class MoneyAction extends NPCShopAction {
public Transaction grant(Entity entity, int repeats) { public Transaction grant(Entity entity, int repeats) {
if (!(entity instanceof Player)) if (!(entity instanceof Player))
return Transaction.fail(); return Transaction.fail();
Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider(); Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider();
Player player = (Player) entity; Player player = (Player) entity;
double amount = money * repeats; double amount = money * repeats;
return Transaction.create(() -> {
return true; return Transaction.create(() -> true, () -> {
}, () -> {
economy.depositPlayer(player, amount); economy.depositPlayer(player, amount);
}, () -> { }, () -> {
economy.withdrawPlayer(player, amount); economy.withdrawPlayer(player, amount);
@ -59,12 +60,12 @@ public class MoneyAction extends NPCShopAction {
public Transaction take(Entity entity, int repeats) { public Transaction take(Entity entity, int repeats) {
if (!(entity instanceof Player)) if (!(entity instanceof Player))
return Transaction.fail(); return Transaction.fail();
Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider(); Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider();
Player player = (Player) entity; Player player = (Player) entity;
double amount = money * repeats; double amount = money * repeats;
return Transaction.create(() -> {
return economy.has(player, amount); return Transaction.create(() -> economy.has(player, amount), () -> {
}, () -> {
economy.withdrawPlayer(player, amount); economy.withdrawPlayer(player, amount);
}, () -> { }, () -> {
economy.depositPlayer(player, amount); economy.depositPlayer(player, amount);
@ -76,12 +77,13 @@ public class MoneyAction extends NPCShopAction {
@Override @Override
public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback) { public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback) {
final MoneyAction action = previous == null ? new MoneyAction() : (MoneyAction) previous; MoneyAction action = previous == null ? new MoneyAction() : (MoneyAction) previous;
return InputMenus.filteredStringSetter(() -> Double.toString(action.money), (s) -> { return InputMenus.filteredStringSetter(() -> Double.toString(action.money), s -> {
try { try {
double result = Double.parseDouble(s); double result = Double.parseDouble(s);
if (result < 0) if (result < 0)
return false; return false;
action.money = result; action.money = result;
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
return false; return false;
@ -100,9 +102,9 @@ public class MoneyAction extends NPCShopAction {
supported = false; supported = false;
} }
} }
if (!supported) { if (!supported)
return null; return null;
}
String description = null; String description = null;
if (previous != null) { if (previous != null) {
MoneyAction old = (MoneyAction) previous; MoneyAction old = (MoneyAction) previous;

View File

@ -45,7 +45,7 @@ public abstract class NPCShopAction implements Cloneable {
private final Runnable rollback; private final Runnable rollback;
public Transaction(Supplier<Boolean> isPossible, Runnable execute, Runnable rollback) { public Transaction(Supplier<Boolean> isPossible, Runnable execute, Runnable rollback) {
this.possible = isPossible; possible = isPossible;
this.execute = execute; this.execute = execute;
this.rollback = rollback; this.rollback = rollback;
} }
@ -88,7 +88,6 @@ public abstract class NPCShopAction implements Cloneable {
GUI.add(gui); GUI.add(gui);
} }
private static final List<GUI> GUI = Lists.newArrayList(); private static List<GUI> GUI = Lists.newArrayList();
private static final PersisterRegistry<NPCShopAction> REGISTRY = PersistenceLoader private static PersisterRegistry<NPCShopAction> REGISTRY = PersistenceLoader.createRegistry(NPCShopAction.class);
.createRegistry(NPCShopAction.class);
} }

View File

@ -56,9 +56,7 @@ public class PermissionAction extends NPCShopAction {
return Transaction.fail(); return Transaction.fail();
Player player = (Player) entity; Player player = (Player) entity;
Permission perm = Bukkit.getServicesManager().getRegistration(Permission.class).getProvider(); Permission perm = Bukkit.getServicesManager().getRegistration(Permission.class).getProvider();
return Transaction.create(() -> { return Transaction.create(() -> true, () -> {
return true;
}, () -> {
for (String permission : permissions) { for (String permission : permissions) {
perm.playerAdd(null, player, Placeholders.replace(permission, player)); perm.playerAdd(null, player, Placeholders.replace(permission, player));
} }
@ -77,9 +75,8 @@ public class PermissionAction extends NPCShopAction {
Permission perm = Bukkit.getServicesManager().getRegistration(Permission.class).getProvider(); Permission perm = Bukkit.getServicesManager().getRegistration(Permission.class).getProvider();
return Transaction.create(() -> { return Transaction.create(() -> {
for (String permission : permissions) { for (String permission : permissions) {
if (!perm.playerHas(player, Placeholders.replace(permission, player))) { if (!perm.playerHas(player, Placeholders.replace(permission, player)))
return false; return false;
}
} }
return true; return true;
}, () -> { }, () -> {
@ -109,7 +106,7 @@ public class PermissionAction extends NPCShopAction {
@Override @Override
public void initialise(MenuContext ctx) { public void initialise(MenuContext ctx) {
for (int i = 0; i < 3 * 9; i++) { for (int i = 0; i < 3 * 9; i++) {
final int idx = i; int idx = i;
ctx.getSlot(i).clear(); ctx.getSlot(i).clear();
if (i < base.permissions.size()) { if (i < base.permissions.size()) {
ctx.getSlot(i).setItemStack(new ItemStack(Material.FEATHER), "<f>Set permission", ctx.getSlot(i).setItemStack(new ItemStack(Material.FEATHER), "<f>Set permission",
@ -124,8 +121,8 @@ public class PermissionAction extends NPCShopAction {
} }
return; return;
} }
ctx.getMenu().transition(InputMenus.stringSetter( ctx.getMenu().transition(InputMenus
() -> idx < base.permissions.size() ? base.permissions.get(idx) : "", (res) -> { .stringSetter(() -> idx < base.permissions.size() ? base.permissions.get(idx) : "", res -> {
if (res == null) { if (res == null) {
if (idx < base.permissions.size()) { if (idx < base.permissions.size()) {
base.permissions.remove(idx); base.permissions.remove(idx);
@ -166,9 +163,8 @@ public class PermissionAction extends NPCShopAction {
supported = false; supported = false;
} }
} }
if (!supported) { if (!supported)
return null; return null;
}
String description = null; String description = null;
if (previous != null) { if (previous != null) {
PermissionAction old = (PermissionAction) previous; PermissionAction old = (PermissionAction) previous;

View File

@ -55,11 +55,11 @@ public class Text extends Trait implements Runnable, Listener {
private boolean speechBubbles; private boolean speechBubbles;
@Persist(value = "talk-close") @Persist(value = "talk-close")
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean(); private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
private final List<String> text = new ArrayList<String>(); private final List<String> text = new ArrayList<>();
public Text() { public Text() {
super("text"); super("text");
this.plugin = CitizensAPI.getPlugin(); plugin = CitizensAPI.getPlugin();
} }
/** /**
@ -87,10 +87,10 @@ public class Text extends Trait implements Runnable, Listener {
/** /**
* Builds a text editor in game for the supplied {@link Player}. * Builds a text editor in game for the supplied {@link Player}.
*/ */
public Editor getEditor(final Player player) { public Editor getEditor(Player player) {
final Conversation conversation = new ConversationFactory(plugin).withLocalEcho(false) Conversation conversation = new ConversationFactory(plugin).withLocalEcho(false).withEscapeSequence("/npc text")
.withEscapeSequence("/npc text").withEscapeSequence("exit").withModality(false) .withEscapeSequence("exit").withModality(false).withFirstPrompt(new TextBasePrompt(this))
.withFirstPrompt(new TextBasePrompt(this)).buildConversation(player); .buildConversation(player);
return new Editor() { return new Editor() {
@Override @Override
public void begin() { public void begin() {
@ -142,11 +142,9 @@ public class Text extends Trait implements Runnable, Listener {
for (DataKey sub : key.getRelative("text").getIntegerSubKeys()) { for (DataKey sub : key.getRelative("text").getIntegerSubKeys()) {
text.add(sub.getString("")); text.add(sub.getString(""));
} }
if (text.isEmpty()) { if (text.isEmpty()) {
populateDefaultText(); populateDefaultText();
} }
range = key.getDouble("range"); range = key.getDouble("range");
} }
@ -178,8 +176,9 @@ public class Text extends Trait implements Runnable, Listener {
return; return;
for (Player player : CitizensAPI.getLocationLookup().getNearbyPlayers(npc.getEntity().getLocation(), range)) { for (Player player : CitizensAPI.getLocationLookup().getNearbyPlayers(npc.getEntity().getLocation(), range)) {
if (player.getGameMode() == GameMode.SPECTATOR) if (player.getGameMode() == GameMode.SPECTATOR) {
continue; continue;
}
talk(player); talk(player);
} }
} }
@ -216,7 +215,6 @@ public class Text extends Trait implements Runnable, Listener {
} }
index = currentIndex++; index = currentIndex++;
} }
if (speechBubbles) { if (speechBubbles) {
HologramTrait trait = npc.getOrAddTrait(HologramTrait.class); HologramTrait trait = npc.getOrAddTrait(HologramTrait.class);
trait.addTemporaryLine(Placeholders.replace(text.get(index), player, npc), trait.addTemporaryLine(Placeholders.replace(text.get(index), player, npc),
@ -271,7 +269,6 @@ public class Text extends Trait implements Runnable, Listener {
cooldowns.remove(player.getUniqueId()); cooldowns.remove(player.getUniqueId());
} }
sendText(player); sendText(player);
int delay = this.delay == -1 int delay = this.delay == -1
@ -280,35 +277,35 @@ public class Text extends Trait implements Runnable, Listener {
: this.delay; : this.delay;
if (delay <= 0) if (delay <= 0)
return; return;
cooldowns.put(player.getUniqueId(), System.currentTimeMillis() + (delay * 50)); cooldowns.put(player.getUniqueId(), System.currentTimeMillis() + delay * 50);
} }
/** /**
* Toggles talking at random intervals. * Toggles talking at random intervals.
*/ */
public boolean toggleRandomTalker() { public boolean toggleRandomTalker() {
return (randomTalker = !randomTalker); return randomTalker = !randomTalker;
} }
/** /**
* Toggles requiring line of sight before talking. * Toggles requiring line of sight before talking.
*/ */
public boolean toggleRealisticLooking() { public boolean toggleRealisticLooking() {
return (realisticLooker = !realisticLooker); return realisticLooker = !realisticLooker;
} }
/** /**
* Toggles using speech bubbles instead of messages. * Toggles using speech bubbles instead of messages.
*/ */
public boolean toggleSpeechBubbles() { public boolean toggleSpeechBubbles() {
return (speechBubbles = !speechBubbles); return speechBubbles = !speechBubbles;
} }
/** /**
* Toggles talking to nearby Players. * Toggles talking to nearby Players.
*/ */
public boolean toggleTalkClose() { public boolean toggleTalkClose() {
return (talkClose = !talkClose); return talkClose = !talkClose;
} }
public boolean useRealisticLooking() { public boolean useRealisticLooking() {
@ -319,5 +316,5 @@ public class Text extends Trait implements Runnable, Listener {
return speechBubbles; return speechBubbles;
} }
private static final Random RANDOM = Util.getFastRandom(); private static Random RANDOM = Util.getFastRandom();
} }

View File

@ -60,7 +60,6 @@ public class TextBasePrompt extends StringPrompt {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_PAGE); Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_PAGE);
} }
} }
Messaging.send(sender, getPromptText(context)); Messaging.send(sender, getPromptText(context));
if (input.equalsIgnoreCase("delay")) { if (input.equalsIgnoreCase("delay")) {
@ -100,9 +99,7 @@ public class TextBasePrompt extends StringPrompt {
} }
} else { } else {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_EDIT_TYPE); Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_EDIT_TYPE);
return this;
} }
return this; return this;
} }
@ -117,7 +114,7 @@ public class TextBasePrompt extends StringPrompt {
colorToggleableText(text.isRandomTalker()), colorToggleableText(text.useSpeechBubbles()), colorToggleableText(text.isRandomTalker()), colorToggleableText(text.useSpeechBubbles()),
colorToggleableText(text.useRealisticLooking()))); colorToggleableText(text.useRealisticLooking())));
int page = context.getSessionData("page") == null ? 1 : (int) context.getSessionData("page"); int page = context.getSessionData("page") == null ? 1 : (int) context.getSessionData("page");
text.sendPage(((Player) context.getForWhom()), page); text.sendPage((Player) context.getForWhom(), page);
return ""; return "";
} }
} }

View File

@ -38,7 +38,7 @@ public class AllayTrait extends Trait {
} }
public void setDancing(boolean dance) { public void setDancing(boolean dance) {
this.dancing = dance; dancing = dance;
} }
@Command( @Command(
@ -61,8 +61,7 @@ public class AllayTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -71,10 +71,9 @@ public class AxolotlTrait extends Trait {
AxolotlTrait trait = npc.getOrAddTrait(AxolotlTrait.class); AxolotlTrait trait = npc.getOrAddTrait(AxolotlTrait.class);
String output = ""; String output = "";
if (args.hasValueFlag("variant")) { if (args.hasValueFlag("variant")) {
if (variant == null) { if (variant == null)
throw new CommandException(Messages.INVALID_AXOLOTL_VARIANT, throw new CommandException(Messages.INVALID_AXOLOTL_VARIANT,
Util.listValuesPretty(Axolotl.Variant.values())); Util.listValuesPretty(Axolotl.Variant.values()));
}
trait.setVariant(variant); trait.setVariant(variant);
output += ' ' + Messaging.tr(Messages.AXOLOTL_VARIANT_SET, args.getFlag("variant")); output += ' ' + Messaging.tr(Messages.AXOLOTL_VARIANT_SET, args.getFlag("variant"));
} }
@ -85,8 +84,7 @@ public class AxolotlTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -75,9 +75,8 @@ public class BeeTrait extends Trait {
BeeTrait trait = npc.getOrAddTrait(BeeTrait.class); BeeTrait trait = npc.getOrAddTrait(BeeTrait.class);
String output = ""; String output = "";
if (anger != null) { if (anger != null) {
if (anger < 0) { if (anger < 0)
throw new CommandException(Messages.INVALID_BEE_ANGER); throw new CommandException(Messages.INVALID_BEE_ANGER);
}
trait.setAnger(anger); trait.setAnger(anger);
output += ' ' + Messaging.tr(Messages.BEE_ANGER_SET, args.getFlag("anger")); output += ' ' + Messaging.tr(Messages.BEE_ANGER_SET, args.getFlag("anger"));
} }
@ -93,8 +92,7 @@ public class BeeTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -69,7 +69,6 @@ public class BossBarTrait extends Trait {
barCache = Bukkit.getServer().createBossBar(npc.getFullName(), color, style, barCache = Bukkit.getServer().createBossBar(npc.getFullName(), color, style,
flags.toArray(new BarFlag[flags.size()])); flags.toArray(new BarFlag[flags.size()]));
} }
return barCache; return barCache;
} }
@ -106,7 +105,6 @@ public class BossBarTrait extends Trait {
if (isBoss) { if (isBoss) {
onDespawn(); onDespawn();
} }
return isBoss; return isBoss;
} }
@ -164,35 +162,30 @@ public class BossBarTrait extends Trait {
bar.setProgress(Math.max(0, Math.min(1, number))); bar.setProgress(Math.max(0, Math.min(1, number)));
} }
} }
bar.setTitle(title); bar.setTitle(title);
bar.setVisible(visible); bar.setVisible(visible);
if (progressProvider != null) { if (progressProvider != null) {
bar.setProgress(progressProvider.get()); bar.setProgress(progressProvider.get());
} }
if (style != null) { if (style != null) {
bar.setStyle(style); bar.setStyle(style);
} }
if (color != null) { if (color != null) {
bar.setColor(color); bar.setColor(color);
} }
for (BarFlag flag : BarFlag.values()) { for (BarFlag flag : BarFlag.values()) {
bar.removeFlag(flag); bar.removeFlag(flag);
} }
for (BarFlag flag : flags) { for (BarFlag flag : flags) {
bar.addFlag(flag); bar.addFlag(flag);
} }
bar.removeAll(); bar.removeAll();
for (Player player : CitizensAPI.getLocationLookup().getNearbyPlayers(npc.getEntity().getLocation(), for (Player player : CitizensAPI.getLocationLookup().getNearbyPlayers(npc.getEntity().getLocation(),
range > 0 ? range : Setting.BOSSBAR_RANGE.asInt())) { range > 0 ? range : Setting.BOSSBAR_RANGE.asInt())) {
if (viewPermission != null && !player.hasPermission(viewPermission)) if (viewPermission != null && !player.hasPermission(viewPermission)) {
continue; continue;
}
bar.addPlayer(player); bar.addPlayer(player);
} }
} }
@ -210,7 +203,7 @@ public class BossBarTrait extends Trait {
} }
public void setProgressProvider(Supplier<Double> provider) { public void setProgressProvider(Supplier<Double> provider) {
this.progressProvider = provider; progressProvider = provider;
} }
public void setRange(int range) { public void setRange(int range) {
@ -226,11 +219,11 @@ public class BossBarTrait extends Trait {
} }
public void setTrackVariable(String variable) { public void setTrackVariable(String variable) {
this.track = variable; track = variable;
} }
public void setViewPermission(String viewpermission) { public void setViewPermission(String viewpermission) {
this.viewPermission = viewpermission; viewPermission = viewpermission;
} }
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
@ -253,31 +246,24 @@ public class BossBarTrait extends Trait {
if (style != null) { if (style != null) {
trait.setStyle(style); trait.setStyle(style);
} }
if (color != null) { if (color != null) {
trait.setColor(color); trait.setColor(color);
} }
if (track != null) { if (track != null) {
trait.setTrackVariable(track); trait.setTrackVariable(track);
} }
if (title != null) { if (title != null) {
trait.setTitle(Messaging.parseComponents(title)); trait.setTitle(Messaging.parseComponents(title));
} }
if (visible != null) { if (visible != null) {
trait.setVisible(visible); trait.setVisible(visible);
} }
if (range != null) { if (range != null) {
trait.setRange(range); trait.setRange(range);
} }
if (viewpermission != null) { if (viewpermission != null) {
trait.setViewPermission(viewpermission); trait.setViewPermission(viewpermission);
} }
if (flags != null) { if (flags != null) {
List<BarFlag> parsed = Lists.newArrayList(); List<BarFlag> parsed = Lists.newArrayList();
for (String s : Splitter.on(',').omitEmptyStrings().trimResults().split(flags)) { for (String s : Splitter.on(',').omitEmptyStrings().trimResults().split(flags)) {

View File

@ -64,7 +64,6 @@ public class CamelTrait extends Trait {
trait.setPose(pose); trait.setPose(pose);
output += Messaging.tr(Messages.CAMEL_POSE_SET, pose); output += Messaging.tr(Messages.CAMEL_POSE_SET, pose);
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output); Messaging.send(sender, output);
} }

View File

@ -54,7 +54,7 @@ public class CatTrait extends Trait {
} }
public void setCollarColor(DyeColor color) { public void setCollarColor(DyeColor color) {
this.collarColor = color; collarColor = color;
} }
public void setLyingDown(boolean lying) { public void setLyingDown(boolean lying) {
@ -74,21 +74,21 @@ public class CatTrait extends Trait {
public void setType(Type type2) { public void setType(Type type2) {
if (type2 == null) { if (type2 == null) {
this.type = Cat.Type.BLACK; type = Cat.Type.BLACK;
return; return;
} }
switch (type2) { switch (type2) {
case WILD_OCELOT: case WILD_OCELOT:
this.type = Cat.Type.CALICO; type = Cat.Type.CALICO;
break; break;
case BLACK_CAT: case BLACK_CAT:
this.type = Cat.Type.BLACK; type = Cat.Type.BLACK;
break; break;
case RED_CAT: case RED_CAT:
this.type = Cat.Type.RED; type = Cat.Type.RED;
break; break;
case SIAMESE_CAT: case SIAMESE_CAT:
this.type = Cat.Type.SIAMESE; type = Cat.Type.SIAMESE;
break; break;
} }
} }
@ -108,22 +108,18 @@ public class CatTrait extends Trait {
CatTrait trait = npc.getOrAddTrait(CatTrait.class); CatTrait trait = npc.getOrAddTrait(CatTrait.class);
String output = ""; String output = "";
if (args.hasValueFlag("type")) { if (args.hasValueFlag("type")) {
if (type == null) { if (type == null)
throw new CommandUsageException(Messages.INVALID_CAT_TYPE, Util.listValuesPretty(Cat.Type.values())); throw new CommandUsageException(Messages.INVALID_CAT_TYPE, Util.listValuesPretty(Cat.Type.values()));
}
trait.setType(type); trait.setType(type);
output += ' ' + Messaging.tr(Messages.CAT_TYPE_SET, args.getFlag("type")); output += ' ' + Messaging.tr(Messages.CAT_TYPE_SET, args.getFlag("type"));
} }
if (args.hasValueFlag("ccolor")) { if (args.hasValueFlag("ccolor")) {
if (ccolor == null) { if (ccolor == null)
throw new CommandUsageException(Messages.INVALID_CAT_COLLAR_COLOR, throw new CommandUsageException(Messages.INVALID_CAT_COLLAR_COLOR,
Util.listValuesPretty(DyeColor.values())); Util.listValuesPretty(DyeColor.values()));
}
trait.setCollarColor(ccolor); trait.setCollarColor(ccolor);
output += ' ' + Messaging.tr(Messages.CAT_COLLAR_COLOR_SET, args.getFlag("ccolor")); output += ' ' + Messaging.tr(Messages.CAT_COLLAR_COLOR_SET, args.getFlag("ccolor"));
} }
if (args.hasFlag('s')) { if (args.hasFlag('s')) {
trait.setSitting(true); trait.setSitting(true);
output += ' ' + Messaging.tr(Messages.CAT_STARTED_SITTING, npc.getName()); output += ' ' + Messaging.tr(Messages.CAT_STARTED_SITTING, npc.getName());
@ -131,17 +127,14 @@ public class CatTrait extends Trait {
trait.setSitting(false); trait.setSitting(false);
output += ' ' + Messaging.tr(Messages.CAT_STOPPED_SITTING, npc.getName()); output += ' ' + Messaging.tr(Messages.CAT_STOPPED_SITTING, npc.getName());
} }
if (args.hasFlag('l')) { if (args.hasFlag('l')) {
trait.setLyingDown(!trait.isLyingDown()); trait.setLyingDown(!trait.isLyingDown());
output += ' ' + Messaging.tr(trait.isLyingDown() ? Messages.CAT_STARTED_LYING : Messages.CAT_STOPPED_LYING, output += ' ' + Messaging.tr(trait.isLyingDown() ? Messages.CAT_STARTED_LYING : Messages.CAT_STOPPED_LYING,
npc.getName()); npc.getName());
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -92,10 +92,9 @@ public class FoxTrait extends Trait {
String output = ""; String output = "";
if (rawtype != null) { if (rawtype != null) {
Fox.Type type = Util.matchEnum(Fox.Type.values(), args.getFlag("type")); Fox.Type type = Util.matchEnum(Fox.Type.values(), args.getFlag("type"));
if (type == null) { if (type == null)
throw new CommandUsageException( throw new CommandUsageException(
Messaging.tr(Messages.INVALID_FOX_TYPE, Util.listValuesPretty(Fox.Type.values())), null); Messaging.tr(Messages.INVALID_FOX_TYPE, Util.listValuesPretty(Fox.Type.values())), null);
}
trait.setType(type); trait.setType(type);
output += ' ' + Messaging.tr(Messages.FOX_TYPE_SET, args.getFlag("type"), npc.getName()); output += ' ' + Messaging.tr(Messages.FOX_TYPE_SET, args.getFlag("type"), npc.getName());
} }
@ -116,8 +115,7 @@ public class FoxTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -57,9 +57,8 @@ public class FrogTrait extends Trait {
FrogTrait trait = npc.getOrAddTrait(FrogTrait.class); FrogTrait trait = npc.getOrAddTrait(FrogTrait.class);
String output = ""; String output = "";
if (args.hasValueFlag("variant")) { if (args.hasValueFlag("variant")) {
if (variant == null) { if (variant == null)
throw new CommandException(Messages.INVALID_FROG_VARIANT, Util.listValuesPretty(Frog.Variant.values())); throw new CommandException(Messages.INVALID_FROG_VARIANT, Util.listValuesPretty(Frog.Variant.values()));
}
trait.setVariant(variant); trait.setVariant(variant);
output += Messaging.tr(Messages.FROG_VARIANT_SET, Util.prettyEnum(variant)); output += Messaging.tr(Messages.FROG_VARIANT_SET, Util.prettyEnum(variant));
} }

View File

@ -50,11 +50,11 @@ public class GoatTrait extends Trait {
} }
public void setLeftHorn(boolean horn) { public void setLeftHorn(boolean horn) {
this.leftHorn = horn; leftHorn = horn;
} }
public void setRightHorn(boolean horn) { public void setRightHorn(boolean horn) {
this.rightHorn = horn; rightHorn = horn;
} }
@Command( @Command(

View File

@ -81,12 +81,10 @@ public class LlamaTrait extends Trait {
trait.setColor(color); trait.setColor(color);
output += Messaging.tr(Messages.LLAMA_COLOR_SET, Util.prettyEnum(color)); output += Messaging.tr(Messages.LLAMA_COLOR_SET, Util.prettyEnum(color));
} }
if (strength != null) { if (strength != null) {
trait.setStrength(Math.max(1, Math.min(5, strength))); trait.setStrength(Math.max(1, Math.min(5, strength)));
output += Messaging.tr(Messages.LLAMA_STRENGTH_SET, trait.getStrength()); output += Messaging.tr(Messages.LLAMA_STRENGTH_SET, trait.getStrength());
} }
if (args.hasFlag('c')) { if (args.hasFlag('c')) {
npc.getOrAddTrait(HorseModifiers.class).setCarryingChest(true); npc.getOrAddTrait(HorseModifiers.class).setCarryingChest(true);
output += Messaging.tr(Messages.HORSE_CHEST_SET) + " "; output += Messaging.tr(Messages.HORSE_CHEST_SET) + " ";
@ -94,7 +92,6 @@ public class LlamaTrait extends Trait {
npc.getOrAddTrait(HorseModifiers.class).setCarryingChest(false); npc.getOrAddTrait(HorseModifiers.class).setCarryingChest(false);
output += Messaging.tr(Messages.HORSE_CHEST_UNSET) + " "; output += Messaging.tr(Messages.HORSE_CHEST_UNSET) + " ";
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output); Messaging.send(sender, output);
} }

View File

@ -71,8 +71,7 @@ public class MushroomCowTrait extends Trait {
Messaging.sendTr(sender, Messages.MUSHROOM_COW_VARIANT_SET, npc.getName(), variant); Messaging.sendTr(sender, Messages.MUSHROOM_COW_VARIANT_SET, npc.getName(), variant);
hasArg = true; hasArg = true;
} }
if (!hasArg) { if (!hasArg)
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -88,11 +88,11 @@ public class PandaTrait extends Trait {
} }
public void setHiddenGene(Panda.Gene gene) { public void setHiddenGene(Panda.Gene gene) {
this.hiddenGene = gene; hiddenGene = gene;
} }
public void setMainGene(Panda.Gene gene) { public void setMainGene(Panda.Gene gene) {
this.mainGene = gene; mainGene = gene;
} }
public void setRolling(boolean rolling) { public void setRolling(boolean rolling) {
@ -138,18 +138,16 @@ public class PandaTrait extends Trait {
PandaTrait trait = npc.getOrAddTrait(PandaTrait.class); PandaTrait trait = npc.getOrAddTrait(PandaTrait.class);
String output = ""; String output = "";
if (args.hasValueFlag("gene")) { if (args.hasValueFlag("gene")) {
if (gene == null) { if (gene == null)
throw new CommandUsageException(Messages.INVALID_PANDA_GENE, throw new CommandUsageException(Messages.INVALID_PANDA_GENE,
Util.listValuesPretty(Panda.Gene.values())); Util.listValuesPretty(Panda.Gene.values()));
}
trait.setMainGene(gene); trait.setMainGene(gene);
output += ' ' + Messaging.tr(Messages.PANDA_MAIN_GENE_SET, args.getFlag("gene")); output += ' ' + Messaging.tr(Messages.PANDA_MAIN_GENE_SET, args.getFlag("gene"));
} }
if (args.hasValueFlag("hiddengene")) { if (args.hasValueFlag("hiddengene")) {
if (hiddengene == null) { if (hiddengene == null)
throw new CommandUsageException(Messages.INVALID_PANDA_GENE, throw new CommandUsageException(Messages.INVALID_PANDA_GENE,
Util.listValuesPretty(Panda.Gene.values())); Util.listValuesPretty(Panda.Gene.values()));
}
trait.setHiddenGene(hiddengene); trait.setHiddenGene(hiddengene);
output += ' ' + Messaging.tr(Messages.PANDA_HIDDEN_GENE_SET, hiddengene); output += ' ' + Messaging.tr(Messages.PANDA_HIDDEN_GENE_SET, hiddengene);
} }
@ -175,9 +173,8 @@ public class PandaTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
private static boolean SUPPORT_ROLLING_SNEEZING = true; private static boolean SUPPORT_ROLLING_SNEEZING = true;

View File

@ -57,9 +57,8 @@ public class ParrotTrait extends Trait {
ParrotTrait trait = npc.getOrAddTrait(ParrotTrait.class); ParrotTrait trait = npc.getOrAddTrait(ParrotTrait.class);
String output = ""; String output = "";
if (args.hasValueFlag("variant")) { if (args.hasValueFlag("variant")) {
if (variant == null) { if (variant == null)
throw new CommandException(Messages.INVALID_PARROT_VARIANT, Util.listValuesPretty(Variant.values())); throw new CommandException(Messages.INVALID_PARROT_VARIANT, Util.listValuesPretty(Variant.values()));
}
trait.setVariant(variant); trait.setVariant(variant);
output += Messaging.tr(Messages.PARROT_VARIANT_SET, Util.prettyEnum(variant)); output += Messaging.tr(Messages.PARROT_VARIANT_SET, Util.prettyEnum(variant));
} }

View File

@ -56,16 +56,14 @@ public class PhantomTrait extends Trait {
PhantomTrait trait = npc.getOrAddTrait(PhantomTrait.class); PhantomTrait trait = npc.getOrAddTrait(PhantomTrait.class);
String output = ""; String output = "";
if (size != null) { if (size != null) {
if (size <= 0) { if (size <= 0)
throw new CommandUsageException(); throw new CommandUsageException();
}
trait.setSize(size); trait.setSize(size);
output += Messaging.tr(Messages.PHANTOM_STATE_SET, size); output += Messaging.tr(Messages.PHANTOM_STATE_SET, size);
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output); Messaging.send(sender, output);
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -61,9 +61,8 @@ public class PiglinTrait extends Trait {
npc.getName()); npc.getName());
hasArg = true; hasArg = true;
} }
if (!hasArg) { if (!hasArg)
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -62,8 +62,7 @@ public class PolarBearTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output); Messaging.send(sender, output);
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -29,7 +29,7 @@ public class PufferFishTrait extends Trait {
} }
public void setPuffState(int state) { public void setPuffState(int state) {
this.puffState = state; puffState = state;
} }
@Command( @Command(

View File

@ -95,8 +95,7 @@ public class ShulkerTrait extends Trait {
Messaging.sendTr(sender, Messages.SHULKER_COLOR_SET, npc.getName(), Util.prettyEnum(color)); Messaging.sendTr(sender, Messages.SHULKER_COLOR_SET, npc.getName(), Util.prettyEnum(color));
hasArg = true; hasArg = true;
} }
if (!hasArg) { if (!hasArg)
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -71,8 +71,7 @@ public class SnifferTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -41,7 +41,7 @@ public class SnowmanTrait extends Trait {
} }
public boolean toggleDerp() { public boolean toggleDerp() {
return this.derp = !this.derp; return derp = !derp;
} }
@Command( @Command(
@ -62,8 +62,7 @@ public class SnowmanTrait extends Trait {
Messaging.sendTr(sender, isDerp ? Messages.SNOWMAN_DERP_SET : Messages.SNOWMAN_DERP_STOPPED, npc.getName()); Messaging.sendTr(sender, isDerp ? Messages.SNOWMAN_DERP_SET : Messages.SNOWMAN_DERP_STOPPED, npc.getName());
hasArg = true; hasArg = true;
} }
if (!hasArg) { if (!hasArg)
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -38,7 +38,6 @@ public class SpellcasterTrait extends Trait {
if (spell != null) { if (spell != null) {
((Spellcaster) npc.getEntity()).setSpell(spell); ((Spellcaster) npc.getEntity()).setSpell(spell);
} }
} }
public void setSpell(Spell spell) { public void setSpell(Spell spell) {
@ -65,8 +64,7 @@ public class SpellcasterTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -56,7 +56,7 @@ public class TropicalFishTrait extends Trait {
} }
public void setBodyColor(DyeColor color) { public void setBodyColor(DyeColor color) {
this.bodyColor = color; bodyColor = color;
} }
public void setPattern(Pattern pattern) { public void setPattern(Pattern pattern) {
@ -64,7 +64,7 @@ public class TropicalFishTrait extends Trait {
} }
public void setPatternColor(DyeColor color) { public void setPatternColor(DyeColor color) {
this.patternColor = color; patternColor = color;
} }
@Command( @Command(
@ -81,33 +81,29 @@ public class TropicalFishTrait extends Trait {
TropicalFishTrait trait = npc.getOrAddTrait(TropicalFishTrait.class); TropicalFishTrait trait = npc.getOrAddTrait(TropicalFishTrait.class);
String output = ""; String output = "";
if (args.hasValueFlag("body")) { if (args.hasValueFlag("body")) {
if (body == null) { if (body == null)
throw new CommandException(Messages.INVALID_TROPICALFISH_COLOR, throw new CommandException(Messages.INVALID_TROPICALFISH_COLOR,
Util.listValuesPretty(DyeColor.values())); Util.listValuesPretty(DyeColor.values()));
}
trait.setBodyColor(body); trait.setBodyColor(body);
output += Messaging.tr(Messages.TROPICALFISH_BODY_COLOR_SET, Util.prettyEnum(body)); output += Messaging.tr(Messages.TROPICALFISH_BODY_COLOR_SET, Util.prettyEnum(body));
} }
if (args.hasValueFlag("patterncolor")) { if (args.hasValueFlag("patterncolor")) {
if (patterncolor == null) { if (patterncolor == null)
throw new CommandException(Messages.INVALID_TROPICALFISH_COLOR, throw new CommandException(Messages.INVALID_TROPICALFISH_COLOR,
Util.listValuesPretty(DyeColor.values())); Util.listValuesPretty(DyeColor.values()));
}
trait.setPatternColor(patterncolor); trait.setPatternColor(patterncolor);
output += Messaging.tr(Messages.TROPICALFISH_PATTERN_COLOR_SET, Util.prettyEnum(patterncolor)); output += Messaging.tr(Messages.TROPICALFISH_PATTERN_COLOR_SET, Util.prettyEnum(patterncolor));
} }
if (args.hasValueFlag("pattern")) { if (args.hasValueFlag("pattern")) {
if (pattern == null) { if (pattern == null)
throw new CommandException(Messages.INVALID_TROPICALFISH_PATTERN, throw new CommandException(Messages.INVALID_TROPICALFISH_PATTERN,
Util.listValuesPretty(Pattern.values())); Util.listValuesPretty(Pattern.values()));
}
trait.setPattern(pattern); trait.setPattern(pattern);
output += Messaging.tr(Messages.TROPICALFISH_PATTERN_SET, Util.prettyEnum(pattern)); output += Messaging.tr(Messages.TROPICALFISH_PATTERN_SET, Util.prettyEnum(pattern));
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output); Messaging.send(sender, output);
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

View File

@ -76,25 +76,22 @@ public class VillagerTrait extends Trait {
VillagerTrait trait = npc.getOrAddTrait(VillagerTrait.class); VillagerTrait trait = npc.getOrAddTrait(VillagerTrait.class);
String output = ""; String output = "";
if (level != null) { if (level != null) {
if (level < 0) { if (level < 0)
throw new CommandUsageException(); throw new CommandUsageException();
}
trait.setLevel(level); trait.setLevel(level);
output += " " + Messaging.tr(Messages.VILLAGER_LEVEL_SET, level); output += " " + Messaging.tr(Messages.VILLAGER_LEVEL_SET, level);
} }
if (args.hasValueFlag("type")) { if (args.hasValueFlag("type")) {
if (type == null) { if (type == null)
throw new CommandException(Messages.INVALID_VILLAGER_TYPE, throw new CommandException(Messages.INVALID_VILLAGER_TYPE,
Util.listValuesPretty(Villager.Type.values())); Util.listValuesPretty(Villager.Type.values()));
}
trait.setType(type); trait.setType(type);
output += " " + Messaging.tr(Messages.VILLAGER_TYPE_SET, args.getFlag("type")); output += " " + Messaging.tr(Messages.VILLAGER_TYPE_SET, args.getFlag("type"));
} }
if (args.hasValueFlag("profession")) { if (args.hasValueFlag("profession")) {
if (profession == null) { if (profession == null)
throw new CommandException(Messages.INVALID_PROFESSION, args.getFlag("profession"), throw new CommandException(Messages.INVALID_PROFESSION, args.getFlag("profession"),
Joiner.on(',').join(Profession.values())); Joiner.on(',').join(Profession.values()));
}
npc.getOrAddTrait(VillagerProfession.class).setProfession(profession); npc.getOrAddTrait(VillagerProfession.class).setProfession(profession);
output += " " + Messaging.tr(Messages.PROFESSION_SET, npc.getName(), args.getFlag("profession")); output += " " + Messaging.tr(Messages.PROFESSION_SET, npc.getName(), args.getFlag("profession"));
} }
@ -103,8 +100,7 @@ public class VillagerTrait extends Trait {
} }
if (!output.isEmpty()) { if (!output.isEmpty()) {
Messaging.send(sender, output.trim()); Messaging.send(sender, output.trim());
} else { } else
throw new CommandUsageException(); throw new CommandUsageException();
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More